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 #

PatternQuarkus Extension
Databasehibernate-orm-panache, jdbc-postgresql
Cacheredis-client
RESTresteasy-reactive-jackson
Reactivemutiny, hibernate-reactive
Healthsmallrye-health
Metricsmicrometer-registry-prometheus
Tracingopentelemetry
Nativenative profile
Kuberneteskubernetes
Testingjunit5, rest-assured

Summary #

  • Quarkus is ideal for local development with its small startup time and memory footprint.
  • Quarkus dev mode with quarkus:dev has 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.properties with Quarkus config (the quarkus. 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-docker extension.
  • Debugging with the JAVA_DEBUG=true environment variable and exposed port 5005.
  • Dev UI at /q/dev/ for interacting with extensions, config, and databases.

← Previous: Spring Boot   Next: Micronaut →

About | Author | Content Scope | Editorial Policy | Privacy Policy | Disclaimer | Contact