DocsJava apps

Java apps

Deploy Spring Boot, Quarkus and other Java web apps with Maven or Gradle, size the JVM heap for your package, bind to $PORT and fix common startup errors.

ZoPanel builds Java web applications from a Maven or Gradle project, runs the resulting jar as a service of the hosting account and puts nginx with SSL in front of it. Use it for Spring Boot, Quarkus or any Java HTTP server that can listen on a port given at startup.

Prerequisites

  • An administrator has installed the Java versions your apps need under Runtimes: the Java (OpenJDK + Maven) card has one button per OpenJDK version the system packages (for example OpenJDK 21). Maven is installed with the first one. Several versions can be installed side by side:

    System OpenJDK versions offered
    Ubuntu 22.04, Ubuntu 24.04 17, 21, 25
    Debian 12 17
    Debian 13 21, 25

    For other Java versions (for example Java 21 on Debian 12, or Java 8), deploy with a Dockerfile.

  • Gradle itself is not installed: the distributions' gradle package is far older than current projects need. Gradle projects must include the Gradle wrapper (gradlew and the gradle/wrapper/ folder) in the repository.

  • The package has Git deploy & apps among its features, and enough RAM (MB) for the build and two JVMs (see Memory below).

What ZoPanel detects

ZoPanel treats a project as Java when its root directory contains pom.xml, build.gradle or build.gradle.kts. It proposes these commands:

Project Build command Start command
Maven mvn -B -q -DskipTests package (or sh mvnw … when mvnw exists) java -Xmx512m -jar "<first jar in target/>"
Gradle sh gradlew --no-daemon -q build -x test (the wrapper is required, see below) java -Xmx512m -jar "<first jar in build/libs/>"
Spring Boot (Maven or Gradle) as above; Gradle uses the bootJar task as above, plus --server.port=$PORT --server.address=127.0.0.1
Quarkus (Maven or Gradle) as above java -Xmx512m -Dquarkus.http.host=127.0.0.1 -Dquarkus.http.port=$PORT -jar target/quarkus-app/quarkus-run.jar (build/quarkus-app/… with Gradle)

The jar is chosen at startup with ls, skipping jars whose names end in -plain.jar (Gradle's jar without dependencies), -sources.jar, -javadoc.jar, -tests.jar and -original.jar. Spring Boot is recognised from spring-boot in pom.xml or org.springframework.boot in the Gradle build file; Quarkus from quarkus-maven-plugin or io.quarkus in pom.xml, or io.quarkus in the Gradle build file. Tests are skipped during the build.

Without gradlew, the proposed build command calls gradle and the deployment stops with "this Gradle project has no Gradle wrapper…". Run gradle wrapper on your computer, commit gradlew and gradle/wrapper/, and deploy again.

Detection runs in a fixed order, so check the detected framework if your repository mixes languages:

  • A package.json or composer.json in the same directory wins: the project is detected as Node.js or PHP. A Gemfile is also checked before Java.
  • A Procfile with a web: line replaces the start command.
  • For a multi-module build, set Root directory to the module that produces the web app, or turn off Auto-detect and write the commands yourself.

Other frameworks, such as Micronaut, are detected as plain Java. They build normally, but check the start command (see below).

Java version

ZoPanel reads the Java version the project asks for and builds and runs the app with that OpenJDK. JAVA_HOME and PATH point at it during the build (Maven, the Gradle wrapper) and when the app runs:

Project Read from
Maven <java.version>, <maven.compiler.release> or <maven.compiler.source> in pom.xml (1.8 means 8)
Gradle JavaLanguageVersion.of(…), JavaVersion.VERSION_… or sourceCompatibility in build.gradle(.kts)

The build log shows the choice ("Using OpenJDK 21"). If that version is not installed, the oldest newer installed JDK is used, since it can still compile and run older code (Java 8 and 11 projects build with OpenJDK 17). If only older JDKs are installed, the deployment stops with "Java 25 is not installed…": install that version under Runtimes. With no version in the build file, the newest installed JDK is used. To choose yourself, turn off Auto-detect and pick the Version. A JAVA_HOME under Environment variables overrides the choice.

Deploy a Java app

  1. In Websites → New website, choose Git deploy, enter the domain and the repository URL. See Git deploy for private repositories and webhooks.
  2. Open the website's Deploy tab. Check Framework and the commands under Build & run.
  3. To change a command, turn off Auto-detect, edit Build command or Start command, and click Save & redeploy.
  4. Add settings under Environment variables (database URL, secrets, profiles).
  5. Click Deploy now and follow the build in the task log.

The release goes live only after it answers on the Health check path (default /) with a status below 500 within 90 seconds. Until then, the previous release keeps serving traffic. Install and build commands may each run for up to 30 minutes.

Port and address

Your app must listen on 127.0.0.1 at the port in the PORT environment variable. ZoPanel sets PORT for every start, and it changes between the two blue/green slots (the second slot uses the base port plus 10000), so never hard-code it.

Spring Boot. The proposed start command already passes --server.port=$PORT --server.address=127.0.0.1. You can also set it in application.properties:

server.port=${PORT}
server.address=127.0.0.1
# nginx sends X-Forwarded-For and X-Forwarded-Proto: use them for redirects and client IPs
server.forward-headers-strategy=native

Quarkus. A Quarkus build produces target/quarkus-app/quarkus-run.jar with its dependencies in target/quarkus-app/lib/ (build/quarkus-app/ with Gradle). The proposed start command runs it and passes the address and port:

java -Xmx512m -Dquarkus.http.host=127.0.0.1 -Dquarkus.http.port=$PORT -jar target/quarkus-app/quarkus-run.jar

If you package an uber-jar instead (quarkus.package.jar.type=uber-jar), turn off Auto-detect and point the start command at it.

Any other server. Read the variable in code, for example:

int port = Integer.parseInt(System.getenv("PORT"));

Memory

Everything the account runs shares the package's RAM (MB): PHP, cron jobs, the Maven or Gradle build and your JVMs. A process that goes over the limit is stopped by the kernel, and ZoPanel restarts the app.

  • The proposed start command sets the heap to 512 MB with -Xmx512m. The JVM uses more than the heap (metaspace, thread stacks, code cache), so the process is always larger than -Xmx.
  • During a deployment the build runs while the current release is still serving, and for a short time the new and the old release run side by side. Plan the package RAM for two running JVMs plus headroom.
  • To change the heap, turn off Auto-detect and edit -Xmx in Start command. Lower it on small packages; raise it only if the package has room for it twice.

You can watch the account's memory under Resources, and the app's memory in the status card of the Deploy tab. See Packages and limits.

Environment variables

Variables under Environment variables are available during the build and at runtime. ZoPanel also sets PORT and HOST=127.0.0.1.

  • Names use letters, digits and _, start with a letter or _, and are at most 64 characters. Values are one line, up to 8,192 characters. Up to 200 variables.
  • Values are stored in a file only root can read and are passed to the service by systemd, never on the command line.

Spring Boot reads environment variables through relaxed binding, so SPRING_DATASOURCE_URL sets spring.datasource.url and SPRING_PROFILES_ACTIVE=prod selects a profile. Quarkus reads variables such as QUARKUS_DATASOURCE_JDBC_URL the same way.

Use 127.0.0.1 as the database host for databases created in ZoPanel. See Databases.

Files and persistent data

The app runs in a sandbox: it can write inside the account's home directory and to its private /tmp, nothing else. Each deployment builds a new release folder, so put uploads and other data that must survive a release in a Persistent paths entry (for example uploads) or in the database. The last 5 releases are kept for Rollback.

Logs

  • Application output on the Deploy tab shows the last 400 lines of stdout and stderr. Turn on Live to follow it.
  • The full Maven or Gradle output of each deployment is in the task log under Tasks.
  • nginx access and error logs are on the website's Logs tab.

Troubleshooting

Message or symptom What to do
"Java is not installed — install it under Runtimes" No JDK on the server. Ask the administrator to install a version on the Java (OpenJDK + Maven) card under Runtimes.
"Java 25 is not installed (installed: …)" The project asks for a newer Java than the server has. Install it under Runtimes if the system offers it, lower the project's Java version, or deploy with a Dockerfile.
"this Gradle project has no Gradle wrapper…" Commit the Gradle wrapper (gradlew, gradle/wrapper/) to the repository.
"OpenJDK 21 is not packaged for this system" when installing The system does not ship that version (see the table above). Pick another version or deploy with a Dockerfile.
UnsupportedClassVersionError The jar was compiled for a newer Java than the JDK that runs it, usually because the version was set by hand. Turn Auto-detect back on, or pick the right Version.
Unable to access jarfile with an empty name No runnable jar in target/ or build/libs/: check the build output, or set the jar in the start command.
"The app did not respond on port … within 90s (it must listen on $PORT)" The app listens on a fixed port (often 8080) or starts too slowly. Bind to $PORT, and reduce startup work or raise the package's CPU.
"The app exited during startup" Read Application output: usually a missing variable, a database that cannot be reached, or no jar found (Unable to access jarfile).
java.lang.OutOfMemoryError: Java heap space Raise -Xmx, within the package RAM.
The app restarts by itself with no Java error The account hit its RAM limit. Lower -Xmx or raise the package's RAM (MB).
Redirects go to http:// or the app sees 127.0.0.1 as the client IP Make the framework trust the forwarded headers (server.forward-headers-strategy=native in Spring Boot).

← Ruby and Rails apps .NET apps →