Quarkus was built for the container era: fast boot, low memory, and an optional native build via GraalVM that starts in milliseconds. Getting those benefits in production means choosing between the JVM and native paths, exposing the right health endpoints, and making the JVM respect container limits. Here's the full picture.
JVM vs native: pick deliberately
Quarkus can run as a regular JVM application or compile to a native executable with GraalVM.
| JVM mode | Native mode | |
|---|---|---|
| Build time | Fast | Slow (minutes) |
| Startup | ~1 s | ~10-50 ms |
| Memory (RSS) | Higher | Much lower |
| Peak throughput | Highest | Slightly lower |
| Best for | Most services, fast CI | Serverless, scale-to-zero, tiny footprints |
For a typical web API deployed continuously, JVM mode is the pragmatic default — fast builds and the JIT eventually gives you the best throughput. Choose native when cold start and memory dominate, such as scale-to-zero workloads.
Building the artifacts
JVM (fast-jar) build:
./mvnw package -DskipTests
# produces target/quarkus-app/quarkus-run.jarNative build via the container-based GraalVM (no local GraalVM install needed):
./mvnw package -Dnative -Dquarkus.native.container-build=trueHealth checks with SmallRye
Add the health extension and Quarkus exposes standard endpoints with no extra code:
./mvnw quarkus:add-extension -Dextensions=smallrye-healthThis gives you /q/health/live and /q/health/ready. Point your platform's liveness probe at the first and readiness at the second. If you add the JDBC datasource extension, a database readiness check is wired automatically.
Dockerfiles
Quarkus generates Dockerfiles under src/main/docker/. For JVM fast-jar:
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"]The layered layout matters: dependencies (lib/) change rarely, so Docker caches that layer and only your application classes re-upload on each build.
For native, the runtime image is tiny:
FROM 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"]Container-aware JVM settings
Modern JVMs respect cgroup limits, but you should still cap the heap relative to the container memory. Rather than hardcoding -Xmx, use a percentage so the same image works across instance sizes:
JAVA_OPTS=-XX:MaxRAMPercentage=75 -XX:+UseG1GCLeaving headroom (75%, not 100%) avoids the OOM killer terminating your pod, since the JVM uses off-heap memory too.
Configuration
Quarkus binds environment variables to config keys. For a database:
QUARKUS_DATASOURCE_DB_KIND=postgresql
QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://host:5432/db
QUARKUS_DATASOURCE_USERNAME=...
QUARKUS_DATASOURCE_PASSWORD=...If your platform provides a single DATABASE_URL, map it in application.properties or split it into the JDBC variables above.
Deploying on PandaStack
- 1Commit the generated JVM Dockerfile (or the native one if you've chosen native mode).
- 2Connect the repo as a container app in the [dashboard](https://dashboard.pandastack.io).
- 3PandaStack builds with rootless BuildKit in an ephemeral Kubernetes Job and deploys via Helm with automatic SSL.
- 4Attach a managed PostgreSQL database; set the
QUARKUS_DATASOURCE_*variables from the injected credentials. - 5Configure probes against
/q/health/liveand/q/health/ready.
If you run native images on a scale-to-zero free-tier app, Quarkus's millisecond startup makes cold starts nearly invisible — a genuinely good pairing.
References
- [Quarkus: Container images guide](https://quarkus.io/guides/container-image)
- [Quarkus: Building native executables](https://quarkus.io/guides/building-native-image)
- [SmallRye Health in Quarkus](https://quarkus.io/guides/smallrye-health)
- [JDK container awareness (JEP-aligned docs)](https://docs.oracle.com/en/java/javase/21/docs/specs/man/java.html)
Quarkus plus scale-to-zero is a great combo for cost-efficient Java. Deploy on PandaStack's free tier with a managed database at [dashboard.pandastack.io](https://dashboard.pandastack.io).