JavaMicroservices
June 7, 2026

Using MicroProfile and Quarkus for Cloud-Native Java Services

Traditional application servers solve many enterprise problems well, but cloud-native services add new expectations.

A service may need to start quickly, fit naturally into containers, expose health and metrics endpoints, document REST APIs, recover from slow downstream systems, and read configuration from environment-specific sources. MicroProfile and Quarkus address those needs while preserving many familiar Java enterprise ideas.

The Problem

Classic Java enterprise applications often assume a large runtime that hosts applications and provides shared services. That model works well in many enterprise environments, but microservices and container platforms have changed the pressure points.

A cloud-native Java service often needs:

  • Configuration that can change between environments.
  • Health and readiness information.
  • Metrics for capacity planning and operations.
  • Tracing for chains of distributed calls.
  • OpenAPI contracts for REST endpoints.
  • Fault tolerance for slow or unavailable dependencies.
  • Packaging that works well in containers.
  • A development workflow that supports fast feedback.

A traditional application server can provide many enterprise features, but not every cloud-native feature is part of the older standard set.

Classic focus:
Application server
  |
  +-- Deployment
  +-- Transactions
  +-- Persistence
  +-- Messaging
  +-- Operations

Cloud-native focus:
Small service
  |
  +-- Config
  +-- Health
  +-- Metrics
  +-- Tracing
  +-- OpenAPI
  +-- Fault tolerance
  +-- Container-friendly packaging

This is where MicroProfile and Quarkus become useful.

Core Idea

MicroProfile extends the enterprise Java programming model with capabilities aimed at microservices and cloud-native systems.

It lives alongside Jakarta EE. It shares familiar APIs such as JAX-RS, JSON-B, and CDI, then adds specifications for concerns that are common in distributed service architectures.

Important MicroProfile areas include:

  • Config, for separating configuration values from application code.
  • Fault Tolerance, for patterns such as retry, fallback, and circuit breaker.
  • OpenAPI, for REST API contracts.
  • OpenTracing, for tracking distributed call chains.
  • Health, for availability checks.
  • Metrics, for exposing operational values.

The practical choice can be summarized like this:

Prefer Jakarta EE when:
Long-term stability, enterprise runtime features, and existing code are the priority.

Prefer MicroProfile when:
Cloud-native behavior, frequent evolution, and microservice support are the priority.

Quarkus is one implementation path for this second style.

Why Quarkus

Quarkus is an open-source Java framework designed for cloud-native applications and microservices. It was created with containers and Kubernetes in mind, and it builds on technologies familiar to Java developers, including RESTEasy, Hibernate, and Vert.x.

One important design principle is that Quarkus moves work to build time when possible. That includes discovery and injection work in its CDI implementation. The result is a runtime that aims to start quickly and use a smaller footprint.

Quarkus also supports optional native executable builds through GraalVM. This is useful when startup time and memory usage are critical deployment concerns.

The other major theme is developer productivity. Quarkus developer mode lets developers change source code, configuration, and resources, then see the result without a full manual recompile and repackage cycle.

Practical Workflow

A typical Quarkus workflow starts by creating a project with the extensions needed by the service.

mvn io.quarkus:quarkus-maven-plugin:create -DprojectGroupId=com.example -DprojectArtifactId=payment-service -DclassName=com.example.PaymentResource -Dpath=/payments -Dextensions=io.quarkus:quarkus-resteasy,io.quarkus:quarkus-resteasy-jackson,io.quarkus:quarkus-hibernate-orm-panache,io.quarkus:quarkus-jdbc-h2

Then run the application in development mode.

./mvnw compile quarkus:dev

The source example uses RESTEasy, so a generated application can expose a simple endpoint during local development. In a real service, that endpoint becomes a resource such as /payments.

REST with Familiar JAX-RS Style

Quarkus keeps the REST programming model close to Jakarta EE. A resource class still uses JAX-RS style annotations.

import jakarta.inject.Inject;
import jakarta.ws.rs.Consumes;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.PathParam;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.MediaType;

@Path("/payments")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class PaymentResource {
    @Inject
    PaymentRepository repository;

    @POST
    public Payment create(Payment payment) {
        repository.persist(payment);
        return payment;
    }

    @GET
    @Path("/{id}")
    public Payment find(@PathParam("id") String id) {
        return repository.find("id", id).firstResult();
    }
}

This is one reason Quarkus can be approachable for teams with Jakarta EE experience. Existing knowledge of CDI, JAX-RS, JSON serialization, and Hibernate still applies.

Configuration Management

Quarkus implements MicroProfile Config. Configuration can come from multiple sources, with higher-priority sources overriding lower-priority ones.

Common sources include:

  • System properties.
  • Environment variables.
  • .env files.
  • application.properties in a config directory.
  • application.properties in src/main/resources.

A simple service configuration can look like this:

quarkus.datasource.db-kind=h2
quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/~/test
quarkus.datasource.username=sa
quarkus.hibernate-orm.packages=com.example

Profiles allow different values for different environments.

%dev.quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/~/dev
%test.quarkus.datasource.jdbc.url=jdbc:h2:tcp://localhost/~/test
%prod.quarkus.datasource.jdbc.url=jdbc:h2:tcp://database-host/~/prod

Quarkus provides default profiles such as dev, test, and prod. Custom profiles can be activated with the quarkus.profile system property or the QUARKUS_PROFILE environment variable.

Packaging Options

Quarkus supports several packaging approaches.

For regular JVM execution, build with Maven.

mvn clean package
java -jar ./target/quarkus-app/quarkus-run.jar

For an Uber JAR style artifact, pass a package property.

mvn clean package -Dquarkus.package.uber-jar=true

For a native executable build, use the native profile.

./mvnw package -Pnative

Native building uses GraalVM. If GRAALVM_HOME is not configured, Quarkus can try a container-based native build when a container runtime such as Podman or Docker is available.

The practical design question is not only how to package the service, but also how the package fits the deployment model. A single self-contained JAR is simple, but container layering can make dependency packaging tradeoffs more visible.

Persistence with Hibernate and Panache

Quarkus supports Hibernate ORM and JDBC driver extensions. The source material highlights support for common relational databases, including H2, MariaDB, Microsoft SQL Server, MySQL, and PostgreSQL.

Panache sits on top of Hibernate ORM to reduce repetitive persistence code.

There are two common styles.

The first is Active Record. The entity extends PanacheEntity and persistence methods are available directly on the entity type.

import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.Entity;

@Entity
public class Payment extends PanacheEntity {
    public String paymentId;
    public String sender;
    public String recipient;
    public double amount;
}

Usage then becomes compact.

Payment payment = new Payment();
payment.paymentId = "payment-100";
payment.sender = "alice";
payment.recipient = "bob";
payment.amount = 42.0;

payment.persist();

Payment loaded = Payment.find("paymentId", "payment-100").firstResult();

The second style is the repository pattern. The entity stays a standard JPA entity, and a repository owns persistence operations.

import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;

@ApplicationScoped
public class PaymentRepository implements PanacheRepository<Payment> {
    public Payment findByPaymentId(String paymentId) {
        return find("paymentId", paymentId).firstResult();
    }
}

Choose Active Record when a simple CRUD-style model is acceptable. Choose repository style when you want a clearer separation between entity data and persistence operations.

Adding OpenAPI

A simple improvement for REST services is to expose OpenAPI and Swagger UI.

./mvnw quarkus:add-extension -Dextensions=quarkus-smallrye-openapi

After enabling the extension, the application exposes API metadata and a Swagger UI endpoint under the Quarkus management path.

/q/openapi
/q/swagger-ui/

This matters because REST APIs need contracts. OpenAPI gives teams a shared description of endpoints, payloads, and operations.

Moving from Jakarta EE to Quarkus

A simple migration path can be incremental when the application already uses familiar standards.

A practical sequence is:

  1. Start with a small REST and JPA module.
  2. Generate a Quarkus project with RESTEasy, JSON binding, Hibernate ORM, Panache, and the needed JDBC driver.
  3. Copy the application classes into the Quarkus source tree.
  4. Move database connection details into application.properties.
  5. Run in developer mode.
  6. Adjust endpoint root paths, because a Quarkus application may not use the same application context name as the old deployment.
  7. Add OpenAPI.
  8. Replace direct EntityManager usage with Panache only where it improves readability.
  9. Keep behavior unchanged while changing runtime style.
  10. Add MicroProfile features such as health, metrics, tracing, and fault tolerance when the service needs them.

This avoids turning migration into a rewrite. The first goal is to move one working capability, then improve the runtime characteristics.

Common Mistakes

One mistake is assuming Quarkus means abandoning enterprise Java knowledge. Many familiar ideas remain useful: CDI, JAX-RS, JSON binding, Hibernate, and standard annotations.

Another mistake is using every extension only because it exists. Extensions should solve a clear problem. A small REST service does not automatically need WebSocket, messaging, native compilation, and every observability feature on day one.

A third mistake is ignoring configuration priority. Values can come from several sources, so teams should document which source wins in each environment.

A fourth mistake is treating native executable builds as the default answer. Native packaging is useful, but it adds build considerations. Standard JVM packaging may be enough for many services.

A fifth mistake is hiding domain logic inside persistence classes. Panache reduces boilerplate, but it should not make business rules harder to find.

Checklist

  • The service has a clear deployment goal.
  • Needed Quarkus extensions are selected deliberately.
  • REST endpoints use a stable resource model.
  • Configuration is externalized.
  • Profiles are used for environment differences.
  • Packaging style matches the deployment environment.
  • Panache style is chosen intentionally.
  • OpenAPI is enabled when API consumers need a contract.
  • Health, metrics, tracing, and fault tolerance are added where operationally useful.
  • Migration from Jakarta EE is incremental instead of a rewrite.

Conclusion

MicroProfile and Quarkus give Java teams a practical bridge from traditional enterprise applications to cloud-native services.

MicroProfile defines the missing service-level concerns: configuration, fault tolerance, OpenAPI, tracing, health, and metrics. Quarkus packages those ideas into a productive framework that still feels familiar to enterprise Java developers.

Use Quarkus when you want smaller service-oriented applications, fast feedback during development, container-friendly packaging, and cloud-native features without throwing away the Jakarta EE skills your team already has.

Share:

Comments0

Home Profile Menu Sidebar
Top