In this article we will learn how to deploy a Quarkus application on top of a Kubernetes cluster. We will start with a minimal REST application and then we will increase its complexity.
Hard requirements:
- A Kubernetes cluster. For the sake of this example we recommend using Minikube (See this article to get started quickly with Minikube: How to build and deploy a Jakarta EE application on Kubernetes ).
- kubectl command line tool
Before creating your Quarkus project, let’s start the Kubernetes environment:
minikube start 😄 minikube v1.24.0 on Fedora 29 ▪ KUBECONFIG=/home/francesco/taskforce/install/auth/kubeconfig . . . . . 🏄 Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default
Then, in order to build the Docker image using as target Minikube’s Docker instance, execute:
$ eval $(minikube docker-env)
Your Kubernetes environment is ready! Let’s bootstrap Quarkus.
Create the Quarkus Project
We will create a basic Quarkus project from the Command Line and we will progressively increase its complexity.
Firstly, let’s create a project “kubernetes-demo”:
mvn io.quarkus.platform:quarkus-maven-plugin:2.6.1.Final:create \ -DprojectGroupId=com.sample \ -DprojectArtifactId=kubernetes-demo \ -DprojectVersion=1.0.0 \ -DclassName="com.sample.RestDemo"
Next, we need to add Kubernetes and Container-Image extensions. If you are running a fully-fledged Kubernetes Cluster add the following extension:
$ mvn quarkus:add-extension -Dextensions="kubernetes, container-image-docker"
On the other hand, if you are running a Local Kubernetes Engine such as Minikube, the following extensions are tailored for your envornment:
$ mvn quarkus:add-extension -Dextensions="minikube, container-image-docker"
As we are using Minikube, we will go for the latter option.
In a nusthell, the kubernetes extension uses the "Always" image pull policy which forces pulling the Image each time. On the other hand, the minikube extension uses the "IfNotPresent" policy which will pull the image if the tag doesn’t already exist locally-
The following additional dependencies are now in your project:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-minikube</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-container-image-jib</artifactId> </dependency>
Your project includes, out of the box, the following REST Endpoint:
@Path("/hello") public class GreetingResource { @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return "Hello RESTEasy"; } }
To build your application and deploy into Kubernetes execute:
$ mvn clean package -Dquarkus.kubernetes.deploy=true
Next, check from the build logs that the Deployment completed successfully:
[INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Deploying to minikube server: https://192.168.49.2:8443/ in namespace: default. [INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Applied: Service kubernetes-demo. [INFO] [io.quarkus.kubernetes.deployment.KubernetesDeployer] Applied: Deployment kubernetes-demo. [INFO] [io.quarkus.deployment.QuarkusAugmentor] Quarkus augmentation completed in 7723ms
A Pod is now running in the default Kubernetes namespace:
$ kubectl get pod NAME READY STATUS RESTARTS AGE kubernetes-demo-85dfff747d-4xnjm 1/1 Running 0 9s
You can access the Pod logs using the “kubectl logs” command. You can also check your Pods from Kubernetes Dashboard:
$ minikube dashboard

Finally, in order to access your Pod from outside the Cluster, we will be using the Service kubernetes-demo:
$ kubectl get svc NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 <none> 443/TCP 47m kubernetes-demo NodePort 10.102.235.40 <none> 80:30962/TCP 83s
The simplest way to do that, is to configure a port forward towards the Kubernetes Service:
$ kubectl port-forward svc/kubernetes-demo 8080:80 Forwarding from 127.0.0.1:8080 -> 8080 Forwarding from [::1]:8080 -> 8080 Handling connection for 8080
Now all requests to localhost:8080 will be forwarded to the the Service kubernetes-demo:
$ curl localhost:8080/hello Hello RESTEasy
Customizing the application Configuration
As next step, we will show how to customize your Endpoint using a ConfigProperty which we will provide through a ConfigMap.
Firstly, modify your Endpoint to include a ConfigProperty:
@ConfigProperty(name = "greeting") String greeting; @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { return greeting; }
You can configure the property “greeting” in the application.properties file. However, in our example, we will show how to set that property using a ConfigMap.
For this purpose, create a file cm.yml to define your ConfigMap object:
apiVersion: v1 kind: ConfigMap metadata: name: rest-demo data: application.properties: |- greeting=HelloFromConfigMap
Next, add to your application.properties a reference to the “rest-demo” ConfigMap:
quarkus.kubernetes-config.enabled=true quarkus.kubernetes-config.config-maps=rest-demo
Finally, include in your pom.xml the quarkus-kubernetes-config extension which allows developers to use Kubernetes ConfigMaps and Secrets as a configuration source:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-kubernetes-config</artifactId> </dependency>
We are done. Before deploying the new application, create the ConfigMap:
$ kubectl create -f cm.yml
Deploy again the application and check that the Endpoint returns the value from the ConfigMap:
$ curl localhost:8080/hello HelloFromConfigMap
Configuring Database Connectivity
The last enhancement to our application will be database connectivity. We will deploy a PostgreSQL Database in our Kubernetes namespace and connect it from our Quarkus application. To do that, here is our checklist:
- Deploy a PostgreSQL Service on Kubernetes
- Configure the Quarkus application to connect to the PostgreSQL service
Let’s start with Database set up!
Deploying PostgreSQL on Kubernetes
To deploy PostgreSQL on Kubernetes there are several valid strategies, such as Helm Charts. To keep our example as simple as possible, we will just create a YAML file for the Deployment, one for the Service and, finally, one to hold the credentials.
Let’s start from the credentials. Create a ConfigMap – postgres-configmap.yml – with the DB Credentials:
apiVersion: v1 kind: ConfigMap metadata: name: postgres-config labels: app: postgres data: POSTGRESQL_USER: quarkus_test POSTGRESQL_PASSWORD: quarkus_test POSTGRESQL_DATABASE: quarkus_test
When running Kubernetes project in a production system, you should use a Secret to store any sensitive information such as username/passwords
Next, create a Deployment with the file postgres-deployment.yml:
kind: Deployment metadata: name: postgres spec: replicas: 1 selector: matchLabels: app: postgres template: metadata: labels: app: postgres spec: containers: - name: postgres image: centos/postgresql-96-centos7:latest imagePullPolicy: "IfNotPresent" ports: - containerPort: 5432 envFrom: - configMapRef: name: postgres-config
Finally, create a Service with the file postgres-service.yml:
apiVersion: v1 kind: Service metadata: name: postgres labels: app: postgres spec: type: NodePort ports: - port: 5432 selector: app: postgres
Now, create all the above objects on your namespace:
$ kubectl create -f postgres-configmap.yml configmap/postgres-config created $ kubectl create -f postgres-service.yml service/postgres created $ kubectl create -f postgres-deployment.yml
Check the Pod status to verify that PostgreSQL is running:
$ kubectl get pods -w NAME READY STATUS RESTARTS AGE postgres-cf987cb96-9r8vl 1/1 Running 0 58s
Set up Quarkus to connect to PostgreSQL
Firstly, we will configure our application to use Hibernate ORM and PostgreSQL JDBC Driver. We will therefore add the following dependencies:
<dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-hibernate-orm</artifactId> </dependency> <dependency> <groupId>io.quarkus</groupId> <artifactId>quarkus-jdbc-postgresql</artifactId> </dependency>
Next, most importantly, you need to include into the application.properties the JDBC Connection Properties matching with our PostgreSQL Database:
%prod.quarkus.datasource.db-kind=postgresql %prod.quarkus.datasource.username=quarkus_test %prod.quarkus.datasource.password=quarkus_test %prod.quarkus.datasource.jdbc.url=jdbc:postgresql://postgres/quarkus_test %prod.quarkus.datasource.jdbc.max-size=10 %prod.quarkus.datasource.jdbc.min-size=1 quarkus.hibernate-orm.database.generation=drop-and-create quarkus.hibernate-orm.log.sql=true
To verify Database Connectivity, we will create a minimal Entity and a Service to store a record in the PostgreSQL DB.
Here is our Greeting Entity:
@Entity public class Greeting { private Long id; private String message; @Id @SequenceGenerator(name = "greetSeq", sequenceName = "greet_id_seq", allocationSize = 1, initialValue = 1) @GeneratedValue(generator = "greetSeq") public Long getId() { return id; } // Getters/Setters omitted for brevity }
A minimalist Service to insert a Greeting into the DB follows here:
@ApplicationScoped public class GreetingService { private static final Logger LOGGER = Logger.getLogger(GreetingService.class.getName()); @Inject EntityManager em; @Transactional public void saveGreeting() { Greeting g = new Greeting(); g.setMessage("An user greeting was sent at" +new Date()); em.persist(g); LOGGER.info("The greeting was saved on DB"); } }
Finally, we will reach the Service every time there is a request for the hello Endpoint:
@Path("/hello") public class GreetingResource { @ConfigProperty(name = "greeting") String greeting; @Inject GreetingService greetingService; @GET @Produces(MediaType.TEXT_PLAIN) public String hello() { greetingService.saveGreeting(); return greeting; } }
Our Quarkus application is ready. Deploy it on Kubernetes:
$ mvn clean package -Dquarkus.kubernetes.deploy=true
Next, wait until also the kubernetes-demo Pod is Running:
$ kubectl get pods NAME READY STATUS RESTARTS AGE kubernetes-demo-7b49df995-p7ctd 1/1 Running 0 7s postgres-cf987cb96-9r8vl 1/1 Running 0 5m54s
Access the Service again using the port-forward:
curl localhost:8080/hello
In conclusion, if every step completed successfully, you will see from the Pod logs the Hibernate logs of your Insert:
2022-01-06 17:31:59,125 INFO [com.sam.GreetingService] (executor-thread-0) The greeting was saved on DB Hibernate: insert into Greeting (message, id) values (?, ?)
Well done! you managed to deploy an Hibernate ORM Quarkus application on the top of a Kubernetes cluster!
If you want to learn how to deploy a Quarkus application on OpenShift, we recommend this article: Deploying Quarkus applications on OpenShift
Source code for this project: https://github.com/fmarchioni/mastertheboss/tree/master/quarkus/kubernetes-demo