> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sevalla.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> These are the docs for Sevalla, a platform for deploying applications, databases, static sites, object storage and load balancers. The dashboard is at https://app.sevalla.com.
> Bold text in steps is an exact dashboard label.
> To act on a Sevalla account, prefer the Sevalla MCP server (https://mcp.sevalla.com/mcp, see https://docs.sevalla.com/ai-agents/overview.md), then the Sevalla CLI (https://docs.sevalla.com/developer-tools/cli.md), then the REST API (https://api-docs.sevalla.com, OpenAPI spec at https://api.sevalla.com/v3/openapi.json).
> Every page index is at https://docs.sevalla.com/llms.txt.

# Deploy Java applications

> How Java and Kotlin applications build and run on Sevalla with Ingot: Maven and Gradle, the Java version, the PORT variable, JVM memory on each size, and Dockerfiles for the JVM.

A Java or Kotlin web application runs on Sevalla as an [application](https://docs.sevalla.com/applications/overview.md): 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](https://docs.sevalla.com/guides/java/spring-boot.md)**
>
> Deploy Spring Boot 4.1 from Spring Initializr with Maven, Actuator health probes and HTTPS behind the proxy.

> **[Quarkus](https://docs.sevalla.com/guides/java/quarkus.md)**
>
> Deploy Quarkus 3.40 on the JVM from code.quarkus.io, with SmallRye Health and graceful shutdown.

> **[Micronaut](https://docs.sevalla.com/guides/java/micronaut.md)**
>
> Deploy Micronaut 5.2 from Micronaut Launch with Gradle, a health probe and graceful shutdown.

> **[Ktor](https://docs.sevalla.com/guides/java/ktor.md)**
>
> 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

| Part | On Sevalla |
| - | - |
| Build | [Ingot](https://docs.sevalla.com/applications/builds/ingot.md), Sevalla's builder, by default. It recognizes a Maven project by `pom.xml`, and a Gradle project by `settings.gradle.kts`, `settings.gradle`, `build.gradle.kts` or `build.gradle`, in Kotlin or Groovy. |
| Build tool | The project's wrapper, `mvnw` or `gradlew`, so the build runs the Maven or Gradle version the project pins. Without a wrapper, Ingot builds with Maven 3 or Gradle 9; Gradle 9 needs Java 17 or later. |
| Build command | `--batch-mode --no-transfer-progress -DskipTests package` for Maven, `--no-daemon assemble` for Gradle. Tests don't run in the image build; run them in CI. |
| Java version | Java 25, Ingot's default line, unless the project sets another. The build plan's **Java** row shows the version and where it came from. See [Pin the Java version](#pin-the-java-version). |
| What runs | Spring Boot 3.3 and later: the jar, unpacked into `app.jar` and `lib/`. Quarkus: its fast-jar, `quarkus-run.jar`, or its uber-jar; Ingot doesn't build native executables. Anything else: the one executable jar the build writes to `target/` or `build/libs/`, or the `-all.jar` of the Gradle Shadow plugin, which the Ktor Gradle plugin applies too. Ingot skips jars that don't run on their own, such as `original-*.jar` and `*-plain.jar`, and fails the build when more than one is left. |
| Start command and port | `java -jar` on the JRE of the same Java line, with the framework's port property set from `PORT`: `-Dserver.port` for Spring Boot, `-Dquarkus.http.port` for Quarkus and `-Dmicronaut.server.port` for Micronaut. Any other jar must read `PORT` itself, as the [Ktor](https://docs.sevalla.com/guides/java/ktor.md) server does. Sevalla sets `PORT` to the web process's port, `8080` by default. |
| Image | `eclipse-temurin:25-jre-resolute` for Java 25: the Java runtime without the compiler, on Ubuntu 26.04. The application runs as user `1000:1000` under `tini`. See [The image Ingot builds](https://docs.sevalla.com/applications/builds/ingot.md#the-image-ingot-builds). |
| Memory | Ingot sets `JAVA_TOOL_OPTIONS` to `-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError`. See [Size the JVM memory](#size-the-jvm-memory). |
| Multi-module builds | Ingot deploys the one module that applies the Spring Boot, Quarkus, Micronaut, shade, Shadow or Ktor plugin, and builds it with the modules it depends on. When more than one module does, set the start command. See [Set the start command](https://docs.sevalla.com/applications/builds/ingot.md#set-the-start-command). |
| Workers, jobs and cron jobs | Every process runs from the same image with its own **Custom start command**, from `/app`. See [Processes](https://docs.sevalla.com/applications/processes.md). |
| Databases | PostgreSQL, MySQL, MariaDB, Redis or Valkey on Sevalla, reached over an [internal connection](https://docs.sevalla.com/databases/connect.md#connect-an-application) that adds `DB_HOST`, `DB_PORT`, `DB_DATABASE`, `DB_USERNAME`, `DB_PASSWORD` and `DB_URL` for SQL databases. JDBC drivers need a `jdbc:` URL, such as `jdbc:postgresql://<host>:<port>/<database>`, so build it from `DB_HOST`, `DB_PORT` and `DB_DATABASE` rather than `DB_URL`. |
| Files | The filesystem resets on every deployment. Keep uploads in [object storage](https://docs.sevalla.com/object-storage/overview.md), or attach a [disk](https://docs.sevalla.com/applications/disks.md). |
| Other build strategies | [Dockerfile](https://docs.sevalla.com/applications/builds/dockerfile.md), [Railpack](https://docs.sevalla.com/applications/builds/railpack.md), [Buildpacks](https://docs.sevalla.com/applications/builds/buildpacks.md) and the deprecated [Nixpacks](https://docs.sevalla.com/applications/builds/nixpacks.md), on the application's **Settings** tab. See [Build with a Dockerfile](#build-with-a-dockerfile). |

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](https://docs.sevalla.com/applications/builds/ingot.md#configure-the-build).
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](https://docs.sevalla.com/applications/builds/ingot.md#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:

```text theme={"theme":{"light":"github-dark-default","dark":"github-dark-default"}}
-XX:MaxRAMPercentage=75 -XX:+ExitOnOutOfMemoryError
```

* `-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](https://docs.sevalla.com/applications/logs.md). 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:

| Guide | Startup time on H1 | Memory in use |
| - | - | - |
| [Spring Boot](https://docs.sevalla.com/guides/java/spring-boot.md) 4.1 | About 15 seconds | About 170 MB |
| [Quarkus](https://docs.sevalla.com/guides/java/quarkus.md) 3.40 | About 4 seconds | About 100 MB |
| [Micronaut](https://docs.sevalla.com/guides/java/micronaut.md) 5.2 | About 5 seconds | About 130 MB |
| [Ktor](https://docs.sevalla.com/guides/java/ktor.md) 3.6 | About 1.5 seconds | About 95 MB |

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](https://docs.sevalla.com/applications/processes.md#health-checks): without one, a new instance receives traffic before the server listens.

To change the flags, set `JAVA_TOOL_OPTIONS` as an [environment variable](https://docs.sevalla.com/applications/environment-variables.md) 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](https://docs.sevalla.com/applications/troubleshooting/runtime-errors.md#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](https://docs.sevalla.com/guides/java/spring-boot.md#deploy-with-a-dockerfile), [Quarkus](https://docs.sevalla.com/guides/java/quarkus.md#deploy-with-a-dockerfile), [Micronaut](https://docs.sevalla.com/guides/java/micronaut.md#deploy-with-a-dockerfile) and [Ktor](https://docs.sevalla.com/guides/java/ktor.md#deploy-with-a-dockerfile). See [Make the image run on Sevalla](https://docs.sevalla.com/applications/builds/dockerfile.md#make-the-image-run-on-sevalla).


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.