Deploying Quarkus applications on OpenShift

Deploying Quarkus 3.x applications to Red Hat OpenShift is fast and straightforward using either the native quarkus-openshift extension or the Eclipse JKube Maven plugin. Quarkus 3 defaults to Jakarta EE (jakarta.* namespace) and leverages RESTEasy Reactive with Mutiny for non-blocking asynchronous streaming. This guide walks you through deploying both JVM and GraalVM native containers to OpenShift.

This tutorial explores how you can deploy Quarkus 3.x applications in containers and, more specifically, on OpenShift PaaS Cloud platforms. There are different approaches to deploy Quarkus applications on OpenShift. In this tutorial, we will discuss them in detail, incorporating updated standards for Jakarta EE (migrated from javax.* to jakarta.*) and RESTEasy Reactive driven by SmallRye Mutiny.

Start by checking out this example, which is an example of a Jakarta REST (JAX-RS) application using modern Hibernate ORM to persist data on a PostgreSQL database:

https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/hibernate-advanced

If you have a look at the directory folder of the example, you will see that it contains the standard structure of all Quarkus applications, including a folder src/main/docker with Dockerfiles. We will focus on Dockerfile.jvm to deliver Java applications and Dockerfile.native to deploy ultra-fast native executables:

src
├── main
│   ├── docker
│   │   ├── Dockerfile.jvm
│   │   ├── Dockerfile.legacy-jar
│   │   ├── Dockerfile.native
│   │   └── Dockerfile.native-micro
│   ├── java
│   │   └── com
│   │       └── mastertheboss
│   │           ├── CustomerEndpoint.java
│   │           ├── CustomerException.java
│   │           ├── Customer.java
│   │           ├── CustomerRepository.java
│   │           ├── OrderEndpoint.java
│   │           ├── OrderRepository.java
│   │           └── Orders.java
│   └── resources
│       ├── application.properties
│       ├── import.sql
│       └── META-INF
│           └── resources
│               ├── index.html
│               ├── order.html
│               └── stylesheet.css
└── test
    └── java
        └── com
            └── mastertheboss
                ├── GreetingResourceTest.java
                └── NativeGreetingResourceIT.java

Notice that in Quarkus 3.x, code base annotations use the Jakarta EE standard (e.g., jakarta.ws.rs.*, jakarta.persistence.*, and jakarta.enterprise.context.*) instead of legacy javax.* dependencies. Furthermore, endpoint execution is powered by RESTEasy Reactive and non-blocking Mutiny types (Uni and Multi) for optimized performance on cloud environments.

To have a quick run of this example on OpenShift, we recommend using Red Hat OpenShift Local (formerly Red Hat CodeReady Containers / CRC), which is introduced in this tutorial: Getting started with Code Ready Containers

Setting up OpenShift

Before you start CRC / OpenShift Local, it is required to allocate sufficient memory (at least 16GB) to the process, as the S2I container building process can be memory and CPU intensive:

$ crc config set memory 16384

Now start your local cluster with:

$ crc start

When your OpenShift cluster is ready, check that you are able to connect using the credentials printed on the console:

$ oc login -u kubeadmin -p kKdPx-pjmWe-b3kuu-jeZm3 https://api.crc.testing:6443

You should now be logged into the default project. Let’s create a new project for our Quarkus application:

$ oc new-project quarkus

The first application service we need to add to our project is a PostgreSQL database. We will create it using the oc command line tool, setting the credentials in one step:

oc new-app -e POSTGRESQL_USER=quarkus -e POSTGRESQL_PASSWORD=quarkus -e POSTGRESQL_DATABASE=quarkusdb postgresql

The PostgreSQL image will be pulled from the default registry, and within a minute you can verify its status:

oc get pods
NAME                      READY   STATUS                      
postgresql-1-xdlwt        1/1     Running

Now we are ready to build and deploy our Quarkus application. We can achieve this in two main modern ways:

  1. Using the built-in Quarkus OpenShift Extension (quarkus-openshift)
  2. Using the Eclipse JKube OpenShift Maven plugin to generate manifests and manage deployment.

Let’s look at both options in detail.

Deploying Quarkus applications on OpenShift using the Quarkus Extension

To deploy a Quarkus 3 application directly to OpenShift, the easiest method is adding the official OpenShift extension alongside RESTEasy Reactive dependencies:

mvn quarkus:add-extension -Dextensions="openshift,resteasy-reactive-jackson"

Next, review your configuration inside src/main/resources/application.properties:

%prod.quarkus.datasource.db-kind=postgresql
%prod.quarkus.datasource.username=quarkus
%prod.quarkus.datasource.password=quarkus
%prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgresql/quarkusdb
%prod.quarkus.datasource.jdbc.max-size=8
%prod.quarkus.datasource.jdbc.min-size=2

quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=true
quarkus.hibernate-orm.sql-load-script=import.sql

quarkus.openshift.expose=true

In Quarkus 3, when running locally in development mode (mvn quarkus:dev), Dev Services will automatically start a PostgreSQL container using Testcontainers without requiring any manual database configuration. For production (specified via %prod. properties), the application links directly to the OpenShift database service.

Ensure your pom.xml relies on RESTEasy Reactive and JDBC extensions:

<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-resteasy-reactive-jackson</artifactId>
</dependency>
<dependency>
    <groupId>io.quarkus</groupId>
    <artifactId>quarkus-jdbc-postgresql</artifactId>
</dependency>

Now it’s time to deploy your application. To trigger a build and trigger deployment on OpenShift for standard JVM mode, execute:

$ mvn clean package -Dquarkus.kubernetes.deploy=true

To build and deploy a hyper-optimized native container image, enable the native profile:

$ mvn clean package -Pnative -Dquarkus.kubernetes.deploy=true

Note: Make sure GRAALVM_HOME and JAVA_HOME (Java 17+ for Quarkus 3) are properly set in your environment. Within a few minutes, OpenShift will complete S2I compilation and spin up your application pod:

$ oc get pods
NAME                          READY   STATUS      RESTARTS   AGE
hibernate-advanced-1-build    0/1     Completed   0          21m
hibernate-advanced-1-deploy   0/1     Completed   0          24m
hibernate-advanced-3-deploy   0/1     Completed   0          20m
hibernate-advanced-3-zd8k6    1/1     Running     0          20m
postgresql-7677796b66-86ps7   1/1     Running     0          27m

Retrieve the exposed route:

$ oc get routes
NAME             HOST/PORT                                 PATH   SERVICES         PORT       TERMINATION   WILDCARD
hibernate-demo   hibernate-demo-quarkus.apps-crc.testing          hibernate-demo   8080-tcp                 None

You can access that host URL in your browser to verify that your Jakarta REST Quarkus service is functioning properly:

Deploying the application as GraalVM native executable in OpenShift

Deploying Quarkus on OpenShift using JKube Maven Plugin

Eclipse JKube Maven Plugin is the active successor to the legacy Fabric8 Maven plugin. It is designed to generate Kubernetes and OpenShift manifests and manage container builds using Docker, JIB, or S2I strategies.

Using JKube with Quarkus 3 is straightforward. You can define an openshift profile inside your pom.xml:

<profile>
    <id>openshift</id>
    <properties>
        <jkube.generator.quarkus.nativeImage>
            true
        </jkube.generator.quarkus.nativeImage>
    </properties>
    <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.eclipse.jkube</groupId>
                    <artifactId>openshift-maven-plugin</artifactId>
                    <version>1.16.0</version>
                    <executions>
                        <execution>
                            <goals>
                                <goal>resource</goal>
                                <goal>build</goal>
                            </goals>
                        </execution>
                    </executions>
                    <configuration>
                        <enricher>
                            <config>
                                <jkube-service>
                                    <type>NodePort</type>
                                </jkube-service>
                            </config>
                        </enricher>
                    </configuration>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</profile>

This profile includes the openshift-maven-plugin, which can enrich OpenShift resource definitions with custom networking or service configurations.

To prepare a native executable deployment on OpenShift via JKube, first create the native executable binary:

mvn package -Pnative -Dquarkus.native.container-build=true -DskipTests=true

Now build the OpenShift image, create resource manifests, and apply them directly to your cluster:

mvn oc:build oc:resource oc:apply -Popenshift

Your build output will display OpenShift target creation details:

[INFO] oc: OpenShift platform detected
[INFO] oc: Using project: quarkus
[INFO] oc: Creating a Service from openshift.yml namespace quarkus name hibernate-advanced
[INFO] oc: Created Service: target/jkube/applyJson/quarkus/service-hibernate-advanced.json
[INFO] oc: Creating a DeploymentConfig from openshift.yml namespace quarkus name hibernate-advanced
[INFO] oc: Created DeploymentConfig: target/jkube/applyJson/quarkus/deploymentconfig-hibernate-advanced.json
[INFO] oc: Creating Route quarkus:hibernate-advanced host: null
[INFO] oc: HINT: Use the command `oc get pods -w` to watch your pods start up
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------

Check that all application pods are successfully up and running:

$ oc get pods
NAME                             READY   STATUS      RESTARTS   AGE
hibernate-advanced-1-9bwjt       1/1     Running     0          15m
hibernate-advanced-1-deploy      0/1     Completed   0          15m
hibernate-advanced-s2i-1-build   0/1     Completed   0          16m
postgresql-1-4hs7z               1/1     Running     3          22m
postgresql-1-deploy              0/1     Completed   0          22m

Inspect the application pod logs to confirm startup using Quarkus 3.x and modern RESTEasy Reactive stack components:

oc logs hibernate-advanced-1-9bwjt 

QUARKUS_OPTS environment variable was not set, using default values of -Xmx24M -Xms16M -Xmn24M
__  ____  __  _____   ___  __ ____  ______ 
 --/ __ \/ / / / _ | / _ \/ //_/ / / / __/ 
 -/ /_/ / /_/ / __ |/ , _/ ,< / /_/ /\ \   
--\___\_\____/_/ |_/_/|_/_/|_|\____/___/   
2024-10-15 09:27:56,101 INFO  [io.agr.pool] (main) Datasource '<default>': Initial size smaller than min. Connections will be created when necessary
   . . . . .
Hibernate: 
    INSERT INTO customer (id, name, surname) VALUES ( nextval('customerId_seq'), 'John','Doe')
Hibernate: 
    INSERT INTO customer (id, name, surname) VALUES ( nextval('customerId_seq'), 'Fred','Smith')
2024-10-15 09:27:56,180 INFO  [io.quarkus] (main) hibernate-advanced 1.0.0.Final native (powered by Quarkus 3.15.1) started in 0.042s. Listening on: http://0.0.0.0:8080
2024-10-15 09:27:56,181 INFO  [io.quarkus] (main) Profile prod activated. 
2024-10-15 09:27:56,181 INFO  [io.quarkus] (main) Installed features: [agroal, cdi, hibernate-orm, jdbc-postgresql, mutiny, narayana-jta, resteasy-reactive, resteasy-reactive-jackson, smallrye-context-propagation]

Finally, retrieve the generated OpenShift Route to test your live endpoint:

oc get routes
NAME             HOST/PORT                                      PATH   SERVICES         PORT   TERMINATION   WILDCARD
hibernate-demo   hibernate-demo-quarkus.apps-crc.testing          hibernate-demo   8080                 None

Frequently Asked Questions (FAQs)

1. How do Quarkus 3 applications handle the migration to Jakarta EE?

In Quarkus 3.x, all Jakarta specifications use the jakarta.* package space instead of legacy javax.*. Standard REST endpoint annotations (@Path, @GET, @POST) belong to jakarta.ws.rs.*, CDI bean annotations belong to jakarta.enterprise.context.*, and ORM entity mapping belongs to jakarta.persistence.*. Quarkus 3 core extensions automatically target Jakarta EE 10 compatibility.

2. What is the advantage of using RESTEasy Reactive over RESTEasy Classic in Quarkus 3?

RESTEasy Reactive is built specifically for Quarkus' non-blocking architecture using Eclipse Vert.x and SmallRye Mutiny. It processes synchronous and asynchronous reactive endpoints without extra overhead, delivering higher throughput, lower memory consumption, and superior native build support compared to RESTEasy Classic.

3. What is the key difference between the quarkus-openshift extension and the Eclipse JKube Maven Plugin?

The quarkus-openshift extension is built natively into Quarkus build steps. It generates OpenShift manifests (DeploymentConfigs, Routes, Services) automatically during regular Maven/Gradle builds without extra XML configuration. Eclipse JKube is a standalone Maven plugin ecosystem suited for standard Java projects or teams preferring explicit plugin executions in their Maven lifecycle.


Recommended Articles

Build High-Performance Java Microservices with Quarkus 1.0: A Comprehensive Guide

Learn how to build robust and reliable Java applications that work on modern infrastructure like containers and cloud using the latest features of Quarkus 1.0. Discover effective solutions for running Java on serverless apps, microservices, containers, FaaS, and the cloud.

Migrate Spring Boot REST Application to Quarkus Using Red Hat Migration Toolkit for Applications (MTA)

Learn how to migrate a Spring Boot REST application to Quarkus using Red Hat's MTA CLI tool.

A Comprehensive Comparison of WildFly Application Server and Quarkus Framework in Enterprise Java

Explore the features and use cases of WildFly and Quarkus for robust Java applications. #WildFly #Quarkus #EnterpriseJava

Create Standalone Quarkus Applications and Powerful Scripts Using JBang & Quarkus Command Mode

Learn how to develop standalone Quarkus applications with JBang and powerful scripts using Quarkus Command Mode. #Quarkus #Java #Microservices #CloudNative