Fullstack k8s application blog series

Book Library Microservice — Quarkus Native Image Using Panache ORM with Postgres Backend

A B Vijay Kumar
Cloud Native Daily
Published in
8 min readOct 25, 2022


Chapter 6: In this blog, we will build a Quarkus native image application to store and fetch selected books, by the user. We will be using Panache ORM to access PostgreSQL to store and retrieve the user preferences and will be using PV & PVCs.

Book Library Microservice, is called by the application, to store and retrieve the user's book preferences. We will be storing the user preferences in the PostgreSQL database. Let’s get started.

Step 1: Generate and configure the Quarkus application

Quarkus boilerplate code can be generated at and or use the command line to generate the boilerplate. Please refer to Quarkus documentation for generating the boilerplate code.

We will need the following dependencies — JSONB, RESTEasy, arc, JUnit, assured, container-image-docker, smallrye, graphQL, jackson, quarkus-hibernate-orm-panache, quarkus-redis-client, quarkus-jdbc-postgresql, kubernetes-config. The pom file should look something like the following screenshot.

Please refer to Chapter 3 for an explanation of some of these dependencies. The ones that are unique for this are listed below:

  • quarkus-jdbc-postgresql — This installs the Postgres data source JDBC drivers. Refer to the documentation here
  • quarkus-hibernate-orm-panache — Panache is an implementation of Hibernate ORM (Object, Relation Mapping). You can read more about this here. This simplifies our job of translating the object operations to SQL queries and statements.

We have to also add the following properties in

  • quarkus.datasource.db-kind = postgresql — This property is required to set the database as PostgreSQL
  • quarkus.datasource.credentials-provider=custom — We will be using the custom credential provider, as we will be fetching the credentials from Vault. We will implement this later in the blog.
  • quarkus.datasource.credentials-provider-name= booklibrary-db-credentials-provider — We are providing the name of the credential provider. We will be implementing this customer provider.

Step 2: Implement a Custom Credential provider

The following is the implementation of the custom credential provider

We are using @Named annotation to specify the name of the custom provider (this is what we used in the in the previous step). As you can see the implementation is very simple. We are picking up the database user and password from dbUser and dbPassword environment variables and set them as properties. This will be used by the PostgreSQL JDBC to log in to our database. These environment variables are injected by Vault. We will see the Vault configuration later in the blog.

Step 3: Setup Postgres Database

3.1 Create StorageClass

For the development environment, we are configuring the storage class as local-storage. when we move to AWS, we will define it differently, we will see that in the later chapters. Here is the Kubernetes manifest for creating the storage class

3.2 Create PersistentVolume

For the development environment, we will be using the local path as the persistent volume, when we move to AWS, we will explore using the native PostgreSQL or using the AWS storage. We will cover that in the later chapters.

We are using the nodeSelector as docker-desktop (this may have to be changed to the respective node you are using in your Kubernetes cluster). The local storage /Users/vijaykumarab/mnt/data is what I am using in my Macbook. you may have to modify that.

3.3 Create PersistentVolumeClaim

This is a standard PVC, we will be using app label select that when we deploy the database.

3.4 Create Postgres Deployment

We will be using PVC for the Postgres deployment. This is a standard Postgres deployment. We also have the option to use the Postgres operator, to keep this blog simple, I am using a standard deployment. It is recommended to either use managed Postgres or an operator in production. We will discuss the operators, in later blogs. Here is the Kubernetes YAML for the deployment

As you can see, it is a standard deployment, we are using postgres:10.4 image, with a volume mount on /var/lib/postgres within the container that, is mapped to the PVC that we just created in the previous step.

3.5 Create a Service to access the Pod

Let's now create the service that will expose this pod.

As you can see, this is a standard Kubernetes service that exposes the database as a NodePort (which can be changed back to ClusterIP, once we are done with our testing, as this database is only accessed internally by our BookLibrary service, and restricted this database to be accessed from outside the cluster).

We can kubectl apply these manifests to create the database, and test it with any Postgres client ( I use Postico, you can find it here)

Step 2: Implement the logic

Getting back to implementing the BookLibrary microservice. Let's define the REST service resources and implement them.

Step 2.1 Define BookLibrary Object (Entity)

This is the Object that gets mapped to the database record. Find below the screenshot of the code

We are defining the following columns in the database (as members)

  • bookID: The unique identifier of the book
  • userID: the unique identifier of the logged-in user (which will be the email id, used to log in)

We will have the mapping of all the bookIDs that are selected by the respective users userID, so that when we query based on the userID we get all the books that are selected by the logged-in user.

This is defined as PanacheEntity, and this gets created, mapped, and managed by Panache. Please refer to the Panache documentation for more details on how ORM works. You can find it here.

Step 2.2 Implement BookLibraryService

Here is the code for the BookLibraryService Implementation

We are defining the service under /booklib path, using @Path annotation

  • Line 24–31 — We are defining and implementing /booklib/get/{userId} resources, that fetched all the books for the user. We are using the Panache extensively to implement this. (It is very important to learn panache before going through this code. I have kept it out of the scope of this blog).
  • Line 33–50 — We are defining and implementing /booklib/add/{userId}/{bookId} resource. This will be called to add a new Book preference that is selected by the user to his/her library. Once again we are using panache to create an ORM entity, setting the values, to the passed parameter values and calling book.persist() to store it in the database.
  • Line 52–73 — We are defining and implementing /booklib/delete/{userId}/{bookid} resource. This is called to delete a book preference from the users’ book library.

Step 3: Build native build and push to Docker

We will be using Quarkus native build to build a native image and package that to a docker image. We will then be pushing the docker image to (dockerhub). The following are the steps

Step 3.1 Build Native Image (GraalVM) using maven

Go to the application folder and run ./mvnw package -Dnative. This will generate a binary file under book-library-service-1.0.0-SNAPSHOT-runner.

Step 3.2 build and push a docker image

The following is the Dockerfile.native (that is originally generated by quarkus, which is modified)

It is a simple set of steps. We are creating a folder /work, copying the binary file book-library-service-1.0.0-SNAPSHOT-runner (that matches target/*-runner) to the work folder, and provide execute permissions. We are then exposing port 8080 and running the binary file.

To build the docker image we run

docker build -f Dockerfile.native -t $IMAGE_NAME:$BUILD_NUMBER .

and push the image to docker, after logging in to the docker hub


Step 4: Deploy the microservice on the k8s cluster

We built and pushed the image to the docker hub in the previous step, we can check and run the image using docker, but let's create the Kubernetes deployment manifests.

Step 4.1 Define the Deployment resource with Vault injection

This is a standard Kubernetes deployment. We are injecting the Postgres configurations, that we stored in Vault. (Please refer to Chapter 2 for more details on how we configured vault). We are asking the vault to inject dbUser and dbPassword into a config file which is created under /vault/secrets/config. We are then sourcing it to our application by changing the entry point of the docker image, with the following command (lines 32–37)

sh -c source /vault/secrets/config && ./booklibraryservice

Step 4.2 Define the Service

The following is the screenshot of the Service Kubernetes resource YAML

This is a straightforward Service manifest, we are exposing the Book library microservice pods on 8080.

We can apply these manifests by running kubectl apply -f

Step 5: Define GitOps pipeline

Let's define the GitOps pipeline for this microservice. Please refer to Chapter 4 and Chapter 5, to understand the GitOps architecture and approach in detail. The GitOps Actions workflow code is in the GitHub repository, you can find it here. The code is self-explanatory if you have already gone through chapters 4 and 5.

You can find the Kubernetes YAML here

You can find the application code here

That's all for now, In the next chapter, we will build the ReactJS application that will consume these microservices. Till then, take care and have fun

Please leave your comments and feedback

HAPPY DEEWALI!!!! Have a safe and wonderful time with your family

See you soon.

Further Reading:



A B Vijay Kumar
Cloud Native Daily

IBM Fellow, Master Inventor, Mobile, RPi & Cloud Architect & Full-Stack Programmer