Spring Boot deploy previews — a live URL for every PR in under 60 seconds
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 in under 60 seconds.
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:
- Add a multi-stage Dockerfile to your repo.
- Connect the repo to PreviewDrop.
- Push a branch. The preview URL appears on the PR.
No CI scripts to maintain, no serverless.yml, no cold-start tuning.
The Dockerfile
The build stage compiles the fat JAR; the run stage boots it on a slim JRE. Here's the shape that works out of the box:
FROM eclipse-temurin:21-jdk AS build
WORKDIR /app
COPY .mvn/ .mvn
COPY mvnw pom.xml ./
RUN ./mvnw dependency:go-offline -B
COPY src ./src
RUN ./mvnw clean package -DskipTests
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY --from=build /app/target/*.jar app.jar
EXPOSE 8080
# PreviewDrop injects PORT; Spring binds to it via server.port
CMD ["sh", "-c", "java ${JAVA_OPTS} -Dserver.port=${PORT:-8080} -jar app.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 CMD above) or set SERVER_PORT. A hard-coded port still works if it matches what the proxy expects, but reading PORT is the safe default.
Commit the Maven wrapper with the executable bit
The build calls ./mvnw, which 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
Add the Dockerfile, connect your repo, and push a branch. The first preview URL lands on your PR in under 60 seconds. See the Spring Boot guide for the full walkthrough, or sign up and deploy your first preview now.
Ready to give every branch a live URL?
Free tier — 2 concurrent previews, no credit card required.
Start free