Java on Previewops
Previewops builds whatever your Dockerfile produces and runs it as a container, so Java needs no special support. What it does need is what every stack needs: read the injected PORT and listen on 0.0.0.0.
| Read the port | System.getenv("PORT") |
| Bind all interfaces | --server.address=0.0.0.0 |
Dockerfile
FROM eclipse-temurin:21-jdk AS build
WORKDIR /src
COPY . .
RUN ./mvnw -q package -DskipTests
FROM eclipse-temurin:21-jre
COPY --from=build /src/target/app.jar /app.jar
ENV PORT=8080
CMD ["sh", "-c", "java -jar /app.jar --server.port=$PORT --server.address=0.0.0.0"]
ENV PORT=8080 is only a default. Previewops overrides it at deploy time, so the app must read the value rather than assume it.
Application code
A typical setup with Spring Boot:
# application.properties
# Spring Boot does not read PORT automatically — it looks for SERVER_PORT
# or server.port. Map it explicitly, or pass --server.port=$PORT on the
# command line as the Dockerfile above does.
server.port=${PORT:8080}
server.address=0.0.0.0
Java-specific pitfalls
- Spring Boot ignores
PORTby default. It readsSERVER_PORTorserver.port— map one of them, or pass--server.port=$PORT. - JVM heap sizing in small containers. Add
-XX:MaxRAMPercentage=75so the JVM sizes its heap to the container limit rather than the host machine. - Slow cold starts. A large Spring context can exceed the readiness window on a first deploy. Trim autoconfiguration if the preview times out before answering.
Configuration
If your Dockerfile is not at the repository root, point at it from .previewops.yaml:
provider: cloud-run
dockerfile: ./docker/Dockerfile
port: 8080
ttl_hours: 24
See configuration.md for every available option.
Do not gate startup behind a migration
Language-agnostic, and the hardest failure here to diagnose:
# Avoid this
CMD ["sh", "-c", "run-migrations && start-server"]
If the migration cannot reach the database — a paused instance, an expired connection string, a cold-start timeout — it never exits, the server never starts, and the port never opens. The deploy itself already succeeded, so the preview shows as Live while nothing answers. Run migrations as a separate, visible step instead.
Related pages
- getting-started.md — your first deploy end to end
- configuration.md —
.previewops.yamlreference - databases.md — connecting a database to a preview
- ui-deploy.md — deploying from the dashboard