Kubernetes is a powerful and popular container orchestration platform that offers numerous benefits for organisations and developers. Many companies out there these days rely on Kubernetes (or some flavour of it including Rancher, OpenShift, etc.) to automate their environments.

PostgreSQL is perfectly suitable to run on Kubernetes. The way to handle a large relational database on a container orchestration platform is to use a thing called an “operator”. It takes care of all important features such as:

  • Running PostgreSQL
  • Backups and recovery
  • Upgrades and updates
  • … a lot more …

A Kubernetes operator is basically supported to be a replacement for a human “operator” handling all things “PostgreSQL” inside a Kubernetes deployment.

There are various operators for PostgreSQL out there. However, today we want to focus on the CYBERTEC PG operator which is available as Open Source inside our Github. repository. Feel free to check it out and run it in your environment without restrictions.

What is important to mention here: We are running using the CYBERTEC PG operator to manage thousands of instances around the world already. In other words: This code is reliable and just works. And if you find an area of improvement, we are open to pull requests.

In this blog you will learn how to use Minikube to run your first PostgreSQL cluster in your own local Kubernetes deployment.

Installing Minikube

Minikube is the easiest way to test Kubernetes locally on your machine. However, the procedure is the same in “real Kubernetes”. To make life easy for you we have included the deployment procedure of Minikube:

[hs@hansmacbook ~]$ curl -LO https://storage.googleapis.com/minikube/releases/latest/minikube-darwin-amd64
[hs@hansmacbook ~]$ sudo install minikube-darwin-amd64 /usr/local/bin/minikube

Now that we have installed Minikube, we can start it. In this case I have used Docker for virtualization. However, things such as VirtualBox are fine as well. Before running the following command make sure Docker is up and running:

[hs@hansmacbook ~]$ minikube start --driver=docker
😄  minikube v1.31.2 on Darwin 13.2
✨  Using the docker driver based on user configuration
📌  Using Docker Desktop driver with root privileges
👍  Starting control plane node minikube in cluster minikube
🚜  Pulling base image ...
    > gcr.io/k8s-minikube/kicbase...:  447.62 MiB / 447.62 MiB  100.00% 25.95 M
🔥  Creating docker container (CPUs=2, Memory=4000MB) ...
🐳  Preparing Kubernetes v1.27.4 on Docker 24.0.4 ...
    ▪ Generating certificates and keys ...
    ▪ Booting up control plane ...
    ▪ Configuring RBAC rules ...
🔗  Configuring bridge CNI (Container Networking Interface) ...
🔎  Verifying Kubernetes components...
    ▪ Using image gcr.io/k8s-minikube/storage-provisioner:v5
🌟  Enabled addons: default-storageclass, storage-provisioner
🏄  Done! kubectl is now configured to use "minikube" cluster and "default" namespace by default

Voila, Minikube is up and running. We can now work on PostgreSQL and test our shiny CYBERTEC PG operator. Deploying PostgreSQL is easiest when using a Helm chart. If you happen to be on Mac OS X it is therefore necessary to ensure that Helm is readily available:

[hs@hansmacbook ~]$ brew install helm
[hs@hansmacbook ~]$ which helm



makes this really easy.
Now we have all we need: Kubernetes, Helm, etc. We can now follow up with:

  • Deploying the CYBERTEC PG Operator
  • Deploying our first cluster
  • Connecting to the cluster.

The entire process is really quick and should not take more than a couple of minutes. Of course this is true for Minikube. Deploying a full Enterprise grade Kubernetes / OpenShift infrastructure requires some more knowledge than just a sample cluster. However, I think this is a good way to get started.

Running your first PostgreSQL cluster on Kubernetes

After deploying a simple Minikube installation we can take a look at the next steps. One good way to start is to take a look at our Github page which has a lot more information.
You will find a lot more information there.

The first thing is to clone the desired Github repo:

git clone https://github.com/cybertec-postgresql/CYBERTEC-operator-tutorials.git
cd CYBERTEC-operator-tutorials

Once we have cloned the repository and enter it we can create a Kubernetes namespace. For those of you who happen to use OpenShift we have also included the OpenShift equivalent:

# kubectl (kubernetes)
kubectl create namespace cpo

# oc (Openshift)
oc create namespace cpo

Then we can deploy all those Helm charts. This is super easy as everything is fully automated inside those charts. The operators and all those additional components will be deployed seamlessly.

# install Helm charts for the operator
helm install cpo -n cpo setup/helm/operator/.
NAME: cpo
LAST DEPLOYED: Mon Nov 6 14:51:46 2023
STATUS: deployed
______________.___.______________________________________________________________ __________ ________ ________ __
\_ ___ \__ | |\______ \_ _____/\______ \__ ___/\_ _____/\_ ___ \ \______ \/ _____/ \_____ \ ______ ________________ _/ |_ ___________
/ \ \// | | | | _/| __)_ | _/ | | | __)_ / \ \/ ______ | ___/ \ ___ ______ / | \\____ \_/ __ \_ __ \__ \\ __\/ _ \_ __ \
\ \___\____ | | | \| \ | | \ | | | \\ \____ /_____/ | | \ \_\ \ /_____/ / | \ |_> > ___/| | \// __ \| | ( <_> ) | \/
\______ / ______| |______ /_______ / |____|_ / |____| /_______ / \______ / |____| \______ / \_______ / __/ \___ >__| (____ /__| \____/|__|
\/\/ \/ \/ \/ \/ \/ \/ \/|__| \/ \/

The operator has been successfully installed and is ready for use.

Support for use can be found here:
Community mailing list: https://groups.google.com/g/cpo-community
Github project: https://github.com/cybertec-postgresql/postgres-pg-operator
Documentation: https://cybertec-postgresql.github.io/CYBERTEC-pg-operator/

Apache License Version 2.0
Copyright 2022-2023 CYBERTEC PostgreSQL International GmbH

Wow, that was it. That is all it takes. The following command (for OpenShift) checks if the operators is indeed around:

# Show Operator Pod
$ oc get pods -n cpo
postgres-operator-8477679896-72d7c 1/1 Running 0 16s 

Finally we can already deploy our first cluster. We can apply the desired YAML file containing our requirements. Here is what the YAML file might contain:

apiVersion: cpo.opensource.cybertec.at/v1
kind: postgresql
  name: cluster-1
  dockerImage: 'docker.io/cybertecpostgresql/cybertec-pg-container:postgres-rocky9-16.0-1-1'
  numberOfInstances: 1
    version: '16'
      password_encryption: "scram-sha-256"
      cpu: 500m
      memory: 500Mi
      cpu: 500m
      memory: 500Mi
  teamId: acid
    size: 5Gi

What we see here is the amount of CPU power we want to be assigned to, which type of storage we want and a lot more. In short: The YAML file describes what kind of PostgreSQL instance we are looking for.

In the next step we can use this file which is also part of our Github repo to make life easy for you and send it to the Kubernetes cluster:

# Create Cluster 
kubectl apply -f cluster-tutorials/single-cluster/postgres.yaml -n cpo
postgresql.cpo.opensource.cybertec.at/cluster-1 created
 oc get pods -n cpo
NAME                                 READY   STATUS              RESTARTS   AGE
cluster-1-0                          0/1     ContainerCreating   0          9s
postgres-operator-86c7f5cd78-zvdw6   1/1     Running             0          5m34s 

If you want to do the same thing on OpenShift and not on Kubernetes use instead:

oc apply -n cpo -f cluster-tutorials/single-cluster/postgres.yaml

Once the new PostgreSQL instance has been deployed we can check what is going on inside Kubernetes to validate that all is working:

# Show Cluster
$ kubectl get postgresqls.cpo.opensource.cybertec.at -n cpo
cluster-1   acid   16        1      5Gi      500m          500Mi            18s   Running

Note that this is optional – it is only needed for demonstration purposes to show that everything is indeed working.

Allowing traffic to your PostgreSQL server

Your first PostgreSQL database is now working inside Kubernetes. The next goal is to be able to connect to this server. To do that we first have to allow traffic to the instance and then figure out which passwords and so on we can use to actually connect and establish a database connection.

The first step is to sort out networking:

# Connect to database
# patch svc to allow traffic via nodeport
$ kubectl patch svc cluster-1 -n cpo -p '{"spec": {"type": "NodePort"}}'
service/cluster-1 patched

Voila, we now got a cluster IP. The service is accessible:

$ kubectl get svc -n cpo
NAME             TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)          AGE
cluster-1        NodePort    <none>        5432:32551/TCP   3m30s
cluster-1-repl   ClusterIP   <none>        5432/TCP         3m30s

Mind the last line in the listing – it reveals that the IP in fact exists and is operational.

Obtaining connection information for PostgreSQL

The next piece of information we need is the password to create a database connection:

# Get Password from secret 
$ kubectl get secret -n cpo
NAME                                                                          TYPE                 DATA   AGE
cpo-monitoring-role                                                           Opaque               2      38m
cpo-replication.cluster-1.credentials.postgresql.cpo.opensource.cybertec.at   Opaque               2      5m54s
postgres.cluster-1.credentials.postgresql.cpo.opensource.cybertec.at          Opaque               2      5m54s
sh.helm.release.v1.cpo.v1                                                     helm.sh/release.v1   1      38m

$ kubectl get secret postgres.cluster-1.credentials.postgresql.cpo.opensource.cybertec.at -n cpo -o jsonpath='{.data.password}' | base64 --decode

But there is more. We can determine the nodes in operation:

$ kubectl get nodes -o wide
minikube   Ready    control-plane   5h25m   v1.27.4   <none>        Ubuntu 22.04.2 LTS   6.5.9-1-MANJARO   docker://24.0.4

Connecting to PostgreSQL in Kubernetes

Voila, our first cluster is now operational. We can already run psql to connect to our shiny new database instance:

# connect to database
$ psql -h -p 32551 -U postgres
Passwort für Benutzer postgres: 
psql (16.0)
SSL-Verbindung (Protokoll: TLSv1.3, Verschlüsselungsmethode: TLS_AES_256_GCM_SHA384, Komprimierung: aus)
Type "help" for help.


# Connect to Database via Pod-Shell
$ kubectl exec --stdin --tty cluster-1-0 -n cpo -- /bin/bash
[postgres@cluster-1-0 ~]$ psql
psql (16.0)
Type "help" for help.

As you can see there are even two methods. We can create a normal database connection using psql and use the same information in our applications. Alternatively we can make use of the Pod-Shell to talk to PostgreSQL via Kubernetes.

Removing PostgreSQL instances from Kubernetes

Deploying new PostgreSQL instances in Kubernetes is easy and the same is true if we want to get rid of those database instances again.

$ kubectl delete postgresqls.cpo.opensource.cybertec.at cluster-1 -n cpo
postgresql.cpo.opensource.cybertec.at "cluster-1" deleted

Finally here is proof that the operation was indeed successful:

$ kubectl get pods -n cpo
NAME                                 READY   STATUS        RESTARTS   AGE
cluster-1-0                          1/1     Terminating   0          3m
postgres-operator-86c7f5cd78-zvdw6   1/1     Running       0          18m

$ kubectl get pods -n cpo
NAME                                 READY   STATUS    RESTARTS   AGE
postgres-operator-86c7f5cd78-zvdw6   1/1     Running   0          18m


Running PostgreSQL is easy and offers a great deal of automation. Our operator is Open Source under an MIT license and can therefore be used free of charge (we of course provide full support). It has proven to work on thousands of systems worldwide – in short: We are talking about reliable, easy to use software. Please make sure that you checkout our blog on a regular basis – we will publish PostgreSQL and Kubernetes related content on a regular basis. So make sure that you don’t miss out.