Spring Boot deploy previews — a live URL for every PR
Spring Boot apps are awkward to preview. Vercel and Netlify have no JVM runtime, so the "serverless Java" path means wrapping a single handler in a Lambda — and losing the embedded Tomcat or Netty server, your @Scheduled jobs, and the warm JVM with live connection pools that your production app actually depends on. PreviewDrop runs your fat JAR directly in a real container, one per pull request, and posts a TLS-terminated URL to the PR — typically within a couple of minutes, depending on how long your Maven or Gradle build takes.
What you get
Push a branch, get a URL. PreviewDrop builds the JAR with Maven or Gradle, boots the embedded server, and drops a live link into the pull request comment. The preview runs the same container shape as production — not a folded-down Lambda handler — so a preview that passes is a preview you can trust.
The mechanics are simple:
- Make sure the app binds the injected
PORT. - Connect the repo to PreviewDrop — it detects Maven or Gradle from
pom.xmlorbuild.gradle. - Push a branch. The preview URL appears on the PR.
No Dockerfile, no CI scripts to maintain, no serverless.yml, no cold-start tuning. PreviewDrop builds the image for you; a Dockerfile in the repo isn't used.
The build and start commands
The detected build compiles the fat JAR and boots it. If you need to control either step, override the Build command and Start command in Project Settings:
# Build command
./mvnw clean package -DskipTests
# Start command — PreviewDrop injects PORT; Spring binds to it via server.port
java ${JAVA_OPTS} -Dserver.port=${PORT} -jar target/*.jar
Three things matter here, and each one is a real gotcha that will bite you the first time you skip it.
Bind to the injected PORT, not hard-coded 8080
PreviewDrop routes traffic to whatever port your app listens on via the PORT env var. Pass -Dserver.port=${PORT} (as in the Start command above) or set server.port=${PORT:8080} in application.properties. A hard-coded 8080 only works if it happens to match the injected PORT, so reading PORT is the safe default.
Commit the Maven wrapper with the executable bit
If your build uses ./mvnw, it must be committed and executable. Windows checkouts often drop the exec bit — restore it once with:
git update-index --chmod=+x mvnw
Gradle users swap in ./gradlew bootJar instead.
Cap the JVM heap
By default the JVM sizes its heap from total host memory, which can exceed a preview container and trigger an OOM kill. Set JAVA_OPTS=-XX:MaxRAMPercentage=75 (or an explicit -Xmx256m) so the heap stays inside the container limit.
Environment variables
Spring Boot's profile system maps cleanly onto preview environments. Point SPRING_PROFILES_ACTIVE at a preview profile so previews can use a lighter config than production, and route the datasource at a shared dev database — or an in-memory H2 profile for UI-only previews:
# application-preview.yml
spring:
datasource:
url: jdbc:h2:mem:preview;MODE=PostgreSQL
jpa:
hibernate:
ddl-auto: create-drop
Set SPRING_PROFILES_ACTIVE=preview on the project and every preview boots against the throwaway database instead of production.
Why not serverless Java
The "serverless Java" pitch sounds fine until you look at what it drops. A Lambda handler has no embedded Tomcat or Netty, no @Scheduled jobs, no hosted background workers, and no warm JVM with live connection pools. Every request pays a cold-start tax, and the code you ship to the preview is not the code you ship to production.
PreviewDrop runs the fat JAR directly. The same container shape as production, used as a per-PR deploy preview. When a reviewer clicks the link, they're exercising the real thing — scheduled tasks, connection pools, and all.
What it costs
PreviewDrop is flat workspace pricing, not per-seat. The Free plan gives you two live previews at a time, one preview per project, and up to three projects, with a four-hour preview lifetime. Starter is $19/mo for five concurrent previews, three previews per project, up to ten projects, and a 24-hour lifetime. Pro and Team extend the lifetime to three and seven days respectively, with more concurrent previews and larger container memory.
The point is that a Spring Boot team can stop merging blind. Every pull request gets a real, running instance of the app — not a screenshot, not a staging server shared across the whole team.
Try it
Bind to PORT, connect your repo, and push a branch. The first preview URL lands on your PR as soon as the build finishes — typically a couple of minutes for a Maven or Gradle app. See the Spring Boot guide for the full walkthrough, or sign up and deploy your first preview now.
Want previews on your own pull requests? Start free — 2 concurrent previews, no credit card.