Deploy Java app jars to your own servers with Spring Boot and Java 21
Spring Boot packages your service as one executable jar, and Deployment takes it from there: the right JDK, a systemd unit with sensible JVM flags, Actuator health checks before the switch, and a previous jar kept ready for rollback. Build on the server with Maven or Gradle, or hand over the artifact your CI produced, and push to release.
What Deployment detects in a Java project
pom.xml or build.gradle (Groovy or Kotlin DSL) marks a Java project. The java.version property, the Gradle toolchain block or maven.compiler.release picks the JDK, with Java 21 as the default LTS and Java 17 for older services. spring-boot-starter-web or spring-boot-starter-webflux marks a Spring Boot web app, and spring-boot-starter-actuator gives Deployment a ready health endpoint. Flyway or Liquibase on the classpath tells it that migrations run at startup. Quarkus and Micronaut apps are recognized the same way and run as plain jars.
- JDK version installed per site (Java 21 or Java 17)
- Maven Wrapper or Gradle Wrapper used for the build when present
- Executable jar path from the build output
- Health check on
/actuator/healthwhen Actuator is on the classpath
What Deployment installs to run Java services
Provisioning hardens the server (firewall, key-only SSH, unattended security updates, swap sized for the JVM), installs an OpenJDK distribution in the version each site needs, and puts nginx or Caddy in front as the reverse proxy with TLS from Let's Encrypt. Maven and Gradle caches live in a shared folder per site, so builds after the first one only fetch what changed. Each service runs as its own system user with its own environment file.
A deploy script for a Spring Boot jar
For Gradle the build line is ./gradlew bootJar --no-daemon. If your pipeline already builds the jar, replace the build with a download of the artifact for the commit. The process command is java $JAVA_OPTS -jar app.jar, where JAVA_OPTS starts from container-aware defaults such as -XX:MaxRAMPercentage=75 and you can tune it in the panel.
cd "$RELEASE_PATH" ./mvnw -B -q -DskipTests package cp target/*.jar app.jar
systemd, scheduled jobs and background consumers
Each service runs as a systemd unit with SuccessExitStatus=143 so a graceful stop is not reported as a failure, Restart=on-failure, and logs from stdout streamed to the panel. Spring's @Scheduled tasks keep running inside the app; if you prefer tasks outside the JVM, add cron jobs that call a command-line runner, with every run logged. Kafka, RabbitMQ or SQS consumers can run as a second unit from the same jar with a different Spring profile.
Zero downtime Java deploys with a health endpoint
JVM services take a few seconds to warm up, which is exactly why a blind restart hurts. Deployment starts the new jar on a fresh port next to the running one, waits until /actuator/health reports UP (readiness, if you expose health groups), and only then switches the proxy. The old process receives SIGTERM, and with server.shutdown=graceful in your config, Spring stops taking new requests and finishes the open ones within spring.lifecycle.timeout-per-shutdown-phase. Failed startups never reach your users. Older releases keep their jars, so rollback is one click. More on zero downtime deployment and deploy from git.
server.shutdown=graceful spring.lifecycle.timeout-per-shutdown-phase=25s management.endpoint.health.probes.enabled=true
PostgreSQL and MySQL for Java services
Create PostgreSQL, MySQL or MariaDB databases with dedicated users and grants, on the same host or on a separate database server with remote access limited to your app servers. Redis is available for caching and sessions. Datasource URLs and passwords live in encrypted environment variables. Flyway or Liquibase migrations run when the new instance starts, before it reports healthy, so a broken migration stops the release instead of breaking production. Backups go to your own S3-compatible storage.
Which plan fits your Java deployments
| Plan | Monthly | Yearly, per month | Servers | Team members |
|---|---|---|---|---|
| Starter | 49 USD | 24 USD | 3 | 1 |
| Team | 199 USD | 99 USD | 15 | 10 |
| Agency | 499 USD | 249 USD | 60 | 30 |
| Enterprise | 1 499 USD | 749 USD | 250 | unlimited |
JVM services like more memory, so many teams run one service per server: Team gives you 15 servers with roles and the audit log. Larger engineering organizations that need SSO (SAML), a 99.9 percent SLA and invoicing choose Enterprise.
Questions about deploying Java apps
Which Java versions are supported?
Java 21 is the default, and Java 17 is available for services that have not moved yet. Each site picks its own JDK, so both can live on one server.
Can I deploy a war file to Tomcat?
Deployment is built around executable jars. Most Spring apps switch to a jar with one packaging change; legacy war deployments can run with an embedded server in the same way.
What if the app needs a long warmup?
Set the health check timeout per site. Deployment waits for the readiness probe to pass before switching, however long your startup takes within that timeout.
Can I try Deployment before I pay?
Yes. The home page has a free one-time demo: paste a public repository URL and see the detected stack, the server plan and the first lines of the deploy script. Plans start at 49 USD a month, or 24 USD a month billed yearly.
Release your next jar without downtime
Connect a server, point it at your repository and push.
$ git push origin main
✓ build release 20261019-142210
✓ health check 200
✓ switch current 0.4 s
$