Getting Started with Quarkus
|
This section documents Quarkus 3.39.x (current) with 3.33.x LTS as the production recommendation, on the Java 17+ baseline, as published at the official Quarkus guides — which are the reference these pages are written and verified against. Quarkus 4 raises the baseline to Java 21 (first release candidate planned for end of September 2026, see the Quarkus blog); readers upgrading past 3.39.x/3.33.x LTS should expect that change. This content was generated with the assistance of AI and should be verified against those official guides before being relied on in production. This section’s bibliography lists the reference material consulted while preparing these pages. |
Quarkus is a Java framework designed from the ground up for containers and Kubernetes rather than for long-lived application servers. This page introduces the problem it solves, the current release model, the four ways to scaffold a project, the layout each one produces, a first REST endpoint written against Quarkus REST, and the everyday inner loop — create, dev, test, package, deploy — that the rest of the Quarkus pages in this section build on.
The problem Quarkus solves
Traditional Java web stacks were designed for a world of long-running application servers: a JVM boots once, warms up its JIT compiler over minutes, and then serves traffic for days or weeks, so a slow start and a large memory footprint barely matter. That trade-off inverts completely in a Kubernetes-native, container-per-request or scale-to-zero world: a pod that takes ten seconds to become ready fails a tight liveness probe, a rolling deployment or autoscaling event that spins up dozens of replicas pays that ten seconds on every one of them, and a large resident-memory footprint (RSS) per instance directly caps how many containers fit on a node — which is exactly what a cloud bill measures. Quarkus attacks both numbers directly: it moves as much work as possible — classpath scanning, annotation processing, bean wiring, reflection configuration — from application startup to build time, so what ships to production is a pre-computed, close-to-final application rather than one that still has to discover its own shape when the JVM boots. The result, on the JVM alone, is startup measured in tenths of a second and materially lower RSS than an equivalent traditional stack; compiled further to a GraalVM native image, the same application starts in single-digit milliseconds with an even smaller footprint, at the cost of a longer build and some reflection-related caveats covered in depth on a dedicated native-image page in this section. Both modes target the same goal: more instances of the application per node, faster horizontal scaling, and a Java runtime that behaves the way Kubernetes' own health checks and autoscalers expect it to.
Quarkus ships a minor release every four to six weeks and designates a Long Term Support (LTS) release roughly every six months for teams that want a longer support window before their next upgrade. As of this writing the current release is 3.39.x, with 3.33.x LTS the recommended baseline for production deployments that prefer stability over the latest minor. Both lines target Java 17 as the minimum baseline today; Quarkus 4 raises that baseline to Java 21, as already noted in the disclaimer above.
Creating a project
Quarkus offers four equally supported ways to scaffold a new project. All four produce the same project shape; pick whichever fits the environment at hand.
Quarkus CLI
The Quarkus CLI is the most direct route once installed. Install it with SDKMAN!:
sdk install quarkus
Then scaffold a Maven project:
quarkus create app com.example:demo
cd demo
The quarkus-maven-plugin
No separate install is needed when Maven is already available — invoke the plugin directly, pinned to a specific Quarkus version:
mvn io.quarkus.platform:quarkus-maven-plugin:3.39.0:create \
-DprojectGroupId=com.example \
-DprojectArtifactId=demo \
-Dextensions="rest"
cd demo
Gradle
The same Quarkus CLI generates a Gradle project with a flag, or the CLI’s create-app goal can be driven directly against Gradle tooling:
quarkus create app com.example:demo --gradle
cd demo
code.quarkus.io
code.quarkus.io is Quarkus' equivalent of Spring Initializr: a web-based generator where the group/artifact coordinates, build tool (Maven or Gradle), Java version, and extensions are picked from checkboxes, producing a downloadable zip with the same layout described below. It is the easiest starting point when no CLI is installed yet.
The generated project layout
Whichever route was used, the result is the same Maven (or Gradle) project:
$ tree demo
demo
├── .dockerignore
├── mvnw
├── mvnw.cmd
├── pom.xml
├── src
│ ├── main
│ │ ├── docker
│ │ │ ├── Dockerfile.jvm
│ │ │ ├── Dockerfile.legacy-jar
│ │ │ ├── Dockerfile.native
│ │ │ └── Dockerfile.native-micro
│ │ ├── java
│ │ │ └── com
│ │ │ └── example
│ │ │ └── GreetingResource.java
│ │ └── resources
│ │ └── application.properties
│ ├── native-test
│ │ └── java
│ └── test
│ └── java
│ └── com
│ └── example
│ └── GreetingResourceTest.java
-
pom.xml— imports thequarkus-bomand declares the requested extensions (restby default) as plain dependencies with no explicit versions, since the BOM pins them all together; also wires in thequarkus-maven-plugin, which drives every./mvnw quarkus:*goal used below. -
src/main/java— application code; the generator seeds it with a singleGreetingResourceREST endpoint. -
src/main/resources/application.properties— externalized configuration as plainkey=valuepairs, for examplequarkus.http.port=8080; this page keeps configuration at that level, with annotation-driven configuration covered separately elsewhere in this section. -
src/main/docker/— ready-made `Dockerfile`s for each packaging mode this page and later ones cover: a regular JVM fast-jar image, a legacy uber-jar image, a native-image build, and a minimal native "micro" base image. -
src/test/java— JVM-mode tests, run with./mvnw testagainst the packaged or dev-mode application. -
src/native-test/java— tests that only make sense against a compiled native executable (starts out empty; populated once native-image testing is introduced). -
.dockerignore— keepstarget/, IDE metadata, and other build artifacts out of the Docker build context used by the Dockerfiles above. -
mvnw/mvnw.cmd— the Maven wrapper, so./mvnw …works without a locally installed Maven matching the project’s expected version.
The first REST endpoint
The generator’s GreetingResource is written against Quarkus REST — Quarkus' own JAX-RS implementation, built
on jakarta.ws.rs, not the older RESTEasy Classic stack:
package com.example;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;
@Path("/hello")
public class GreetingResource {
@GET
@Produces(MediaType.TEXT_PLAIN)
public String hello() {
return "Hello from Quarkus REST";
}
}
Live coding with quarkus dev
Start the application in dev mode:
./mvnw quarkus:dev
This boots the application, opens the Dev UI at
http://localhost:8080/q/dev/, and enables live coding: editing GreetingResource.java — say, changing the
returned string — and then simply refreshing http://localhost:8080/hello in a browser (or re-issuing
curl http://localhost:8080/hello) recompiles and reloads the change on that next request, with no manual
restart and no separate build step. This edit-save-refresh loop, rather than a slow "stop the JVM, rebuild,
restart" cycle, is one of the concrete productivity gains the build-time-work philosophy from the first section
buys in practice.
Adding an extension
Extensions are the unit of integration in Quarkus — a datasource, a messaging connector, health checks, and so on each arrive as one. Add one with the Maven plugin:
./mvnw quarkus:add-extension -Dextensions="quarkus-smallrye-health"
or, equivalently, with the CLI:
quarkus extension add smallrye-health
Either form edits pom.xml to add the corresponding dependency; dev mode picks it up automatically the next
time a request triggers a rebuild, with no restart required.
Running and packaging
Besides ./mvnw quarkus:dev for local development, a packaged application runs as a plain executable jar:
./mvnw package
java -jar target/quarkus-app/quarkus-run.jar
./mvnw package produces a fast-jar layout under target/quarkus-app/, not a single uber-jar:
target/quarkus-app
├── app/
├── lib/
├── quarkus/
└── quarkus-run.jar
quarkus-run.jar is a thin launcher; lib/ holds the application’s dependency jars (split into a boot and a
main classpath internally so unchanging third-party jars are not repacked on every build); app/ holds the
project’s own compiled classes; quarkus/ holds Quarkus' own generated bootstrap and build-time metadata. This
split lets container image layers cache lib/ separately from the frequently-changing app/ layer, which is
one reason fast-jar images rebuild and push faster than a single fat jar would — the full mechanics of fast-jar,
its --mutable-jar and uber-jar alternatives, and its interaction with the Dockerfiles under src/main/docker/
are covered in depth on a packaging-focused page later in this section.
The inner loop
Day-to-day Quarkus development cycles through the same five steps regardless of which of the four scaffolding routes started the project:
create scaffolds the project once; from there, quarkus dev and the live-coding loop described above dominate
day-to-day work, ./mvnw test (or the native-test module, for native-image-specific tests) validates changes,
./mvnw package produces the target/quarkus-app/ layout described above, and deploy ships one of the
src/main/docker/ images to its target environment — after which the loop returns to dev for the next change.