Quarkus #
Quarkus is a Java framework designed for Kubernetes-native and GraalVM native images. With super-fast startup times and a small memory footprint, Quarkus has become a top choice for cloud-native applications. Docker Compose lets developers run Quarkus with its dependencies (database, cache) in one isolated environment.
This article covers setting up Quarkus with Docker Compose for local development.
Prerequisites #
- Docker and Docker Compose (latest versions)
- Java 21+ for development
- Maven or Gradle
A standard Quarkus project (from code.quarkus.io or mvn io.quarkus.platform:quarkus-maven-plugin:3.6.4:create):
quarkus-app/
├── src/
│ ├── main/
│ │ ├── java/
│ │ └── resources/
│ └── test/
├── pom.xml
└── mvnw
Dockerfiles for Quarkus #
Quarkus has built-in Docker build tooling that generates optimal Dockerfiles. Or you can write one manually.
Using Quarkus build tooling (recommended):
# Add the container-image extension
./mvnw quarkus:add-extension -Dextensions="container-image-docker"
# Build the image
./mvnw package -DskipTests -Dquarkus.container-image.build=true
Quarkus automatically generates a Dockerfile and builds an image tagged per configuration.
Manual multi-stage Dockerfile:
# syntax=docker/dockerfile:1.6
FROM eclipse-temurin:21-jdk-alpine AS builder
WORKDIR /app
COPY pom.xml mvnw ./
COPY .mvn .mvn
RUN chmod +x mvnw && ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests
FROM eclipse-temurin:21-jre-alpine
WORKDIR /app
COPY --from=builder /app/target/quarkus-app/ /app/
EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/quarkus-run.jar"]
Dockerfile for native builds (GraalVM):
FROM ghcr.io/graalvm/graalvm-ce:java21-22.3.0 AS builder
WORKDIR /app
COPY pom.xml mvnw ./
COPY .mvn .mvn
RUN chmod +x mvnw && ./mvnw dependency:go-offline
COPY src ./src
RUN ./mvnw package -DskipTests -Pnative
FROM alpine:3.19
RUN apk add --no-cache libc6-compat
WORKDIR /app
COPY --from=builder /app/target/quarkus-app /app/quarkus-app
EXPOSE 8080
ENTRYPOINT ["/app/quarkus-app"]
Native images start in ~15ms with ~30MB memory.
docker-compose.yml for Development #
services:
app:
build:
context: .
dockerfile: Dockerfile
target: builder
image: quarkus-app:dev
container_name: quarkus-app
command: ./mvnw quarkus:dev
volumes:
- ./src:/app/src
- ./pom.xml:/app/pom.xml
- target:/app/target
ports:
- "8080:8080"
- "5005:5005" # debug port
environment:
- QUARKUS_DATASOURCE_JDBC_URL=jdbc:postgresql://db:5432/quarkus
- QUARKUS_DATASOURCE_USERNAME=app
- QUARKUS_DATASOURCE_PASSWORD=dev
- QUARKUS_REDIS_HOSTS=redis://cache:6379
- JAVA_ENABLE_DEBUG=true
- JAVA_DEBUG=true
depends_on:
db:
condition: service_healthy
cache:
condition: service_healthy
db:
image: postgres:16-alpine
environment:
- POSTGRES_USER=app
- POSTGRES_PASSWORD=dev
- POSTGRES_DB=quarkus
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: ["CMD-SHELL", "pg_isready -U app -d quarkus"]
interval: 10s
timeout: 5s
retries: 5
ports:
- "5432:5432"
cache:
image: redis:7-alpine
healthcheck:
test: ["CMD", "redis-cli", "ping"]
interval: 10s
timeout: 3s
retries: 3
ports:
- "6379:6379"
volumes:
db-data:
target:
Quarkus Dev Mode #
The ./mvnw quarkus:dev command activates the powerful Quarkus Dev Mode:
- Live reload — source code changes trigger restarts in <1 second
- Continuous testing — tests run automatically when source code changes
- Dev UI — interactive UI at http://localhost:8080/q/dev/
- Database migration — runs automatically at startup
- Dev Services — automatically spins up services (Postgres, Redis, Kafka) via Testcontainers when absent
Quarkus Dev Services are very powerful. If you don’t define quarkus.datasource.db-kind or connection details in the dev profile, Quarkus automatically starts a Postgres container via Testcontainers for development. This means the Compose file for external services can be minimal.The Quarkus Dev UI #
Dev UI is a web-based UI for Quarkus dev mode.
Access at http://localhost:8080/q/dev/. Features:
- Extensions — list installed extensions, view config
- Configuration — view runtime config, edit dev-only config
- Endpoints — list JAX-RS endpoints
- Database — browse entities, run queries
- Continuous Testing — view running tests
- Dev Services — view auto-started services
Databases with Hibernate ORM and Panache #
Quarkus + Hibernate ORM + Panache is a powerful combo for database access.
application.properties:
# Database
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=app
quarkus.datasource.password=dev
quarkus.datasource.jdbc.url=jdbc:postgresql://db:5432/quarkus
# Hibernate
quarkus.hibernate-orm.database.generation=update
quarkus.hibernate-orm.log.sql=true
Entity:
@Entity
public class User extends PanacheEntity {
public String email;
public String name;
public static List<User> findByEmail(String email) {
return find("email", email).list();
}
}
Repository:
@ApplicationScoped
public class UserRepository {
@Inject
EntityManager em;
public List<User> findAll() {
return em.createQuery("FROM User", User.class).getResultList();
}
public Optional<User> findById(Long id) {
return Optional.ofNullable(em.find(User.class, id));
}
@Transactional
public User save(User user) {
em.persist(user);
return user;
}
}
For dev, quarkus.hibernate-orm.database.generation=update automatically updates the schema. For production, use Flyway or Liquibase.
Health Checks and Metrics #
Quarkus has built-in extensions for health and metrics.
application.properties:
quarkus.smallrye-health.root-path=/q/health
quarkus.micrometer.export.prometheus.path=/q/metrics
Endpoints:
/q/health/live— liveness probe/q/health/ready— readiness probe/q/health/started— startup probe/q/metrics— Prometheus metrics
Testing with Testcontainers #
Quarkus testing can use Testcontainers automatically.
UserResourceTest.java:
@QuarkusTest
class UserResourceTest {
@Test
void shouldListUsers() {
given()
.when().get("/users")
.then()
.statusCode(200)
.body("size()", is(0));
}
@Test
void shouldCreateUser() {
given()
.contentType(ContentType.JSON)
.body("{\"email\":\"[email protected]\",\"name\":\"Test\"}")
.when().post("/users")
.then()
.statusCode(201);
}
}
Quarkus Dev Services automatically spin up a Postgres container for tests, so no special Compose file is needed.
Build and Run #
# Build
docker compose build
# Run
docker compose up -d
# View logs
docker compose logs -f app
# Stop
docker compose down
Access:
- App: http://localhost:8080
- Dev UI: http://localhost:8080/q/dev/
- Postgres: localhost:5432
- Redis: localhost:6379
Best Practices #
Use Dev Services for Dependencies #
For development, Dev Services automatically manage Postgres/Redis/Kafka containers. The Compose file can be minimal.
Native Images for Production #
Quarkus + GraalVM native images are ideal for production: ~15ms startup, ~30MB memory, small images.
Health Endpoints #
Configure liveness, readiness, and started probes for Kubernetes. Quarkus does this automatically.
Database Migration Tools #
For production, use Flyway. For dev, update mode is enough.
Resource Limits #
services:
app:
deploy:
resources:
limits:
memory: 512M
Native images are memory-efficient. JVM mode needs more.
Quarkus with Kubernetes (k8s) Locally #
Quarkus has an extension for automatic Kubernetes deployment.
./mvnw quarkus:add-extension -Dextensions="kubernetes"
application.properties:
quarkus.kubernetes.deploy=true
quarkus.kubernetes.image-name=quarkus-app
quarkus.kubernetes.image-tag=1.0.0
quarkus.kubernetes.replicas=3
The build automatically applies to the active Kubernetes cluster. For local testing, use kind or minikube.
Quarkus with GraalVM Native in CI/CD #
For native image builds in CI/CD, Quarkus supports building inside containers.
./mvnw package -DskipTests \
-Pnative \
-Dquarkus.native.container-build=true
Quarkus automatically pulls the image quay.io/quarkus/ubi-quarkus-native-image:21.3.0-java17 and runs the native build inside the container. No need to install GraalVM on the host.
Reactive Quarkus with Mutiny #
Quarkus uses Mutiny for reactive programming.
@GET
@Path("/users")
public Uni<List<User>> listUsers() {
return User.listAll(); // returns Uni
}
@GET
@Path("/users/{id}")
public Uni<User> getUser(@PathVariable Long id) {
return User.findById(id)
.onItem().ifNull().failWith(() -> new NotFoundException());
}
Mutiny is more ergonomic than RxJava or Reactor for Quarkus developers.
The Quarkus CLI #
Quarkus has an official CLI that simplifies project management.
# Install the Quarkus CLI
sdk install quarkus
# Create a project
quarkus create app my-app
# Add an extension
quarkus extension add hibernate-orm-panache
# Dev mode (with Tilt/docker-compose integration)
quarkus dev
quarkus dev automatically detects changes, restarts, and can even deploy directly to k8s.
Reactive Databases with Reactive Hibernate #
For reactive workloads, Quarkus has Hibernate Reactive.
quarkus.hibernate-orm.database.generation=update
quarkus.datasource.reactive.url=postgresql://db:5432/quarkus
@GET
@Path("/users")
public Uni<List<User>> listUsers() {
return User.<User>listAll(Sort.by("name"));
}
Reactive Hibernate uses an async driver (vertx-pg-client) for non-blocking database access.
Scheduled Tasks #
Quarkus supports scheduled tasks.
@ApplicationScoped
public class CleanupTask {
@Scheduled(every = "1h", identity = "cleanup-task")
void cleanup() {
// runs every hour
// e.g. remove expired sessions
}
@Scheduled(cron = "0 0 2 * * ?")
void dailyReport() {
// runs at 2 AM every day
}
}
Scheduled tasks run on Quarkus’ scheduler thread. Safe to run in containers with multiple replicas — Quarkus uses leader election to prevent duplicates.
OpenTelemetry Tracing #
quarkus.otel.enabled=true
quarkus.otel.traces.exporter=otlp
quarkus.otel.exporter.otlp.traces.endpoint=http://jaeger:4317
Quarkus automatically instruments HTTP, JDBC, and more. Spans are sent to Jaeger or other platforms.
Recap Cheatsheet #
| Pattern | Quarkus Extension |
|---|---|
| Database | hibernate-orm-panache, jdbc-postgresql |
| Cache | redis-client |
| REST | resteasy-reactive-jackson |
| Reactive | mutiny, hibernate-reactive |
| Health | smallrye-health |
| Metrics | micrometer-registry-prometheus |
| Tracing | opentelemetry |
| Native | native profile |
| Kubernetes | kubernetes |
| Testing | junit5, rest-assured |
Summary #
- Quarkus is ideal for local development with its small startup time and memory footprint.
- Quarkus dev mode with
quarkus:devhas live reload, continuous testing, and the Dev UI.- Dev Services automatically spin up Postgres/Redis/Kafka via Testcontainers when dependencies aren’t defined.
- Multi-stage Dockerfiles with a builder stage (JDK + Maven) and a runtime stage (JRE + JAR).
- Native images with GraalVM for production: ~15ms startup, ~30MB memory, small images.
- Hibernate ORM + Panache for database access with the repository pattern.
- Built-in health endpoints:
/q/health/live,/q/health/ready,/q/health/started.- Testcontainers spun up automatically for tests.
- Configuration via
application.propertieswith Quarkus config (thequarkus.prefix).- Best practices: Dev Services for deps, native images for prod, Flyway for migrations, resource limits matching the mode (JVM vs native).
- Build tooling — Quarkus automatically generates optimal Dockerfiles via the
container-image-dockerextension.- Debugging with the
JAVA_DEBUG=trueenvironment variable and exposed port 5005.- Dev UI at
/q/dev/for interacting with extensions, config, and databases.