Quarkus: Java that starts fast
Quarkus was built for the cloud-native, container era. It does ahead-of-time work at build time so the JVM does less at runtime, giving startup times and memory footprints that traditional Spring-on-JVM struggles to match — and it can compile to a GraalVM native image for truly tiny, instant-start binaries. Both deployment modes work well on a container platform; let's cover each.
Step 1: A REST endpoint and config
// GreetingResource.java
@Path("/health")
public class HealthResource {
@GET
@Produces(MediaType.APPLICATION_JSON)
public Map<String, String> health() {
return Map.of("status", "ok");
}
}Quarkus reads application.properties. Bind the HTTP port to the injected PORT:
# application.properties
quarkus.http.host=0.0.0.0
quarkus.http.port=${PORT:8080}Step 2: Option A — JVM container
The simplest path is a JVM image. Quarkus generates layered output for good Docker caching:
FROM eclipse-temurin:21-jre
WORKDIR /app
COPY target/quarkus-app/lib/ /app/lib/
COPY target/quarkus-app/*.jar /app/
COPY target/quarkus-app/app/ /app/app/
COPY target/quarkus-app/quarkus/ /app/quarkus/
EXPOSE 8080
CMD ["java", "-jar", "/app/quarkus-run.jar"]Build the app first: ./mvnw package. The layered structure means dependency layers cache between builds.
Step 2: Option B — GraalVM native image
For minimal memory and instant startup (great for scale-to-zero), compile native:
./mvnw package -Dnative -Dquarkus.native.container-build=trueFROM quay.io/quarkus/quarkus-micro-image:2.0
WORKDIR /app
COPY target/*-runner /app/application
EXPOSE 8080
CMD ["./application", "-Dquarkus.http.host=0.0.0.0"]Native images start in tens of milliseconds and use a fraction of the memory — ideal on the free tier where apps scale to zero on spot nodes. The trade-off is longer build times.
Step 3: Wire a managed Postgres
Add the JDBC extension (quarkus-jdbc-postgresql) and configure the datasource from env. PandaStack injects DATABASE_URL when you attach a managed PostgreSQL — map it to Quarkus's datasource properties:
quarkus.datasource.db-kind=postgresql
quarkus.datasource.jdbc.url=${JDBC_DATABASE_URL}
quarkus.datasource.username=${DB_USER}
quarkus.datasource.password=${DB_PASSWORD}
quarkus.datasource.jdbc.max-size=10JDBC URLs differ slightly from the standard postgres:// form, so either set JDBC_DATABASE_URL explicitly or translate the injected connection details. Keep max-size below your tier limit (50 free, 300 Pro, 1000 Premium).
Step 4: Schema management
Use Flyway (quarkus-flyway) for migrations, run at startup or as a release step:
quarkus.flyway.migrate-at-start=trueFor multi-replica deploys, prefer a dedicated migration job over migrate-at-start to avoid concurrent migration races.
Step 5: Deploy
Connect your repo and push:
git push origin mainThe build runs in a rootless BuildKit K8s Job pod, the image ships to Artifact Registry, and Helm deploys it. Live logs stream in real time, automatic SSL covers your domain, and server-side metrics come built in.
Step 6: Health checks and observability
Add quarkus-smallrye-health for liveness/readiness endpoints at /q/health — the orchestrator uses these for rollout. Quarkus also exposes metrics via Micrometer if you want app-level metrics alongside the platform's server-side metrics.
JVM vs native: which to pick
| JVM image | Native image | |
|---|---|---|
| Startup | Sub-second to seconds | Tens of ms |
| Memory | Higher | Very low |
| Build time | Fast | Slow |
| Best for | Steady traffic, dev velocity | Scale-to-zero, density |
Start with the JVM image for fast iteration; switch to native when you want the cold-start and memory wins on the free tier.
References
- [Quarkus — Container images](https://quarkus.io/guides/container-image)
- [Quarkus — Building a native executable](https://quarkus.io/guides/building-native-image)
- [Quarkus — Datasources](https://quarkus.io/guides/datasource)
- [Quarkus — SmallRye Health](https://quarkus.io/guides/smallrye-health)
---
Quarkus gives Java a genuinely cloud-native deploy story, especially with native images on scale-to-zero infrastructure. Push one to PandaStack's [free tier](https://dashboard.pandastack.io), attach a managed Postgres, and wire your datasource to the injected connection details.