Bootstrap FreeKB - Postgres (SQL) - Deploy Postgres on OpenShift
Postgres (SQL) - Deploy Postgres on OpenShift

Updated:   |  Postgres (SQL) articles

If you are not familiar with the oc command, refer to OpenShift - Getting Started with the oc command.

Config Map

Since the name of the Postgres database and the username are not sensitive data, these values can be stored in a Config Map. Check out my article FreeKB - OpenShift - Create Config Map. Let's say you have the following in a YAML file.

apiVersion: v1
kind: ConfigMap
metadata:
  name: postgres
  labels:
    app: postgres
data:
  POSTGRESQL_DATABASE: my-postgres-db
  POSTGRESQL_USER: admin

 

The oc apply command with the -f or --filename option can be used to create the config map using the template YAML file.

~]$ oc apply --filename configmap.yml
configmap/postgres created

 

The oc get configmaps command can be used to list the config maps that have been created.

~]$ oc get configmaps
NAME                 DATA   AGE
postgres             2      11m

 

And you can validate that the config maps contains the postgres database name and username.

~]$ oc get configmap postgres --output jsonpath="{.data}"
{"POSTGRESQL_DATABASE":"my-postgres-db","POSTGRESQL_USER":"admin"}

 

Secret

Since the Postgres password is sensitive data, this should be created in a secret. Check out my article FreeKB - OpenShift - Create Opaque Generic Secrets (key value pairs). Let's say you want your admin password to be itsasecret. The base64 command can be used to get the base64 of itsasecret.

~]$ echo itsasecret | base64
aXRzYXNlY3JldAo=

 

Let's say you have the following in a YAML file. Notice the value is aXRzYXNlY3JldAo=, which is the base64 of itsasecret.

apiVersion: v1
kind: Secret
metadata:
  name: postgres-secret
  labels:
    app: postgres
data:
  POSTGRESQL_PASSWORD: aXRzYXNlY3JldAo=

 

The oc apply command with the -f or --filename option can be used to create the secret using the template YAML file.

~]$ oc create --filename secret.yml 
secret/postgres-secret created

 

The oc get secrets command can be used to list the secrets that have been created.

~]# oc get secrets
NAME                 TYPE        DATA      AGE
postgres-secret      Opaque      1         30s

 

And you can validate that the secret contains the base64 password.

]$ oc get secret postgres-secret --output jsonpath="{.data}"
{"POSTGRESQL_PASSWORD":"aXRzYXNlY3JldAo="}

 

Service

By default, postgres listens for connections on port 5432. Check out my article FreeKB - OpenShift - Create ClusterIP Service. Let's say you have the following in a YAML file.

apiVersion: v1
kind: Service
metadata:
  name: postgres
  labels:
    app: postgres
spec:
  selector:
    app: postgres
  ports:
    - protocol: TCP
      port: 5432
      targetPort: 5432
  type: ClusterIP

 

The oc apply command with the -f or --filename option can be used to create the service using the template YAML file.

~]$ oc create --filename service.yml 
service/postgres created

 

The oc get services command can be used to list the services that have been created.

]$ oc get services
NAME          TYPE           CLUSTER-IP      EXTERNAL-IP   PORT(S)             AGE
postgres      ClusterIP      10.11.12.13   <none>        5432/TCP            24m

 

Persistent Volume and Persistent Volume Claim

First and foremost, a Persistent Volume will be needed for storage for the Postgres database. Check out my article FreeKB - OpenShift - Create Persistent Volume Claim (pvc) for details on how to create a Persistent Volume (pv) and Persistent Volume Claim (pvc). The oc get storageclass command can be used to list the available storage classes.

storageClassName must be an exact match of one of the storage classes returned by the oc get storageclass command.

~]$ oc get storageclass
NAME                     PROVISIONER                    RECLAIMPOLICY   VOLUMEBINDINGMODE   ALLOWVOLUMEEXPANSION   AGE
file-storage (default)   csi.trident.netapp.io          Delete          Immediate           true                   377d
thin                     kubernetes.io/vsphere-volume   Delete          Immediate           false                  381d

 

accessMode can contain one or more of the following:

  • ReadWriteOnce (RWO) - The volume may only be mounted on a single node
  • ReadWriteOncePod (RWOP) - The volume may only be mounted on a single pod
  • ReadOnlyMany (ROX) - The volume may be mounted on different nodes
  • ReadWriteMany (RWX) - The volume can be mounted on different nodes

In this example, the file-storage storage class is used.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgres-pvc
  namespace: my-project
spec:
  accessModes:
  - ReadOnlyOnce
  - ReadWriteOnce
  resources:
    requests:
      storage: 5Gi
  storageClassName: file-storage

 

The oc apply command with the -f or --filename option can be used to create the persistent volume claim using the template YAML file.

~]$ oc apply -f create_pvc.yml 
persistentvolumeclaim/postgres-pvc created

 

The oc get persistentvolumeclaims (or oc get pvc) command will return the list of persistent volume claims. The important thing is to ensure the Persistent Volume Claim is bound to a Persistent Volume.

~]$ oc get pvc --output wide
NAME          STATUS   VOLUME                                     CAPACITY   ACCESS MODES   STORAGECLASS   AGE      VOLUMEMODE
postges-pvc   Bound    pvc-2db07c57-e282-48e7-bfb1-4cbd7245c25e   1Gi        RWM            file-storage   3m29s    Filesystem

 

And the oc get persistentvolumes (or oc get pv) command should list the Persistent Volume.

~]$ oc get pv pvc-2db07c57-e282-48e7-bfb1-4cbd7245c25e
NAME                                       CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                     STORAGECLASS   REASON   AGE
pvc-2db07c57-e282-48e7-bfb1-4cbd7245c25e   5Gi        RWM            Delete           Bound    my-project/postgres-pvc   file-storage            19m

 

Stateful Set

Now that we have the config map, secret, and persistent volume / persistent volume claim, we can create the stateful set. 

The oc get images command can be used to list the postgres images that have been imported into your OpenShift cluster.

]$ oc get images | grep -i postgres
sha256:0b939c13john.doe43a85b813ec8c170adcb7479507ed4e592d982296f745c62e   registry.redhat.io/rhel8/postgresql-15@sha256:0b939c13john.doe43a85b813ec8c170adcb7479507ed4e592d982296f745c62e
sha256:12b1d5a86864d21d6594384edfe5cccc94205dbf689fc12345678901237060a5   registry.redhat.io/rhel8/postgresql-13@sha256:12b1d5a86864d21d6594384edfe5cccc94205dbf689fc12345678901237060a5
sha256:1d1d42ba374084d59308f44aafdab883145b06ca109aa7c2f362a578a6abf318   registry.redhat.io/rhel9/postgresql-13@sha256:1d1d42ba374084d59308f44aafdab883145b06ca109aa7c2f362a578a6abf318
sha256:72a202313710713359429b9bbb172c4363efe32d9a1490d25f5e313041022c7a   registry.redhat.io/rhel8/postgresql-13@sha256:72a202313710713359429b9bbb172c4363efe32d9a1490d25f5e313041022c7a
sha256:a1c75f08c929a4992816b8b91f9372852aefe46e183d3cf0464dd5dbe3d836a1   registry.redhat.io/rhel8/postgresql-10@sha256:a1c75f08c929a4992816b8b91f9372852aefe46e183d3cf0464dd5dbe3d836a1
sha256:a21e2a6d92d4ec02421219d6a382f379a12d3ee7a8abe3e74fc01ba0cf56a232   registry.redhat.io/rhscl/postgresql-10-rhel7@sha256:a21e2a6d92d4ec02421219d6a382f379a12d3ee7a8abe3e74fc01ba0cf56a232
sha256:bbe3dd60fefb35dbaf2ee8f45d0b0b903fc5ceab9f58fb522ebd3d9aee4f535f   registry.redhat.io/rhel8/postgresql-12@sha256:bbe3dd60fefb35dbaf2ee8f45d0b0b903fc5ceab9f58fb522ebd3d9aee4f535f
sha256:c6fde1a8653a597c18b0326bc71ce4a614273be74b9aef3ced83a1b11472687a   registry.redhat.io/rhscl/postgresql-13-rhel7@sha256:c6fde1a8653a597c18b0326bc71ce4a614273be74b9aef3ced83a1b11472687a
sha256:d05fd5870e03045baeb1113c913b290e56616a307a559c067c6a57cfcb60e1a2   registry.redhat.io/rhel9/postgresql-15@sha256:d05fd5870e03045baeb1113c913b290e56616a307a559c067c6a57cfcb60e1a2
sha256:d99bb8c63bbdbbfb1bd1a1f4e1a5f114dc5a0d972cdd9d27ca9e69c16cb98089   registry.redhat.io/rhscl/postgresql-12-rhel7@sha256:d99bb8c63bbdbbfb1bd1a1f4e1a5f114dc5a0d972cdd9d27ca9e69c16cb98089

 

Let's say you have the following in a YAML file. You may need to update image to reference one of the postgres images in your cluster

apiVersion: apps/v1
kind: StatefulSet
metadata:
  name: postgres
  labels:
    app: postgres
spec:
  replicas: 1
  serviceName: postgres
  selector:
    matchLabels:
      app: postgres
  template:
    metadata:
      labels:
        app: postgres
    spec:
      containers:
        - env:
            - name: POSTGRESQL_PASSWORD
              valueFrom:
                secretKeyRef:
                  name: postgres-secret
                  key: POSTGRESQL_PASSWORD   
          envFrom:
            - configMapRef:
                name: postgres                     
          name: postgres
          image: registry.redhat.io/rhel9/postgresql-15
          imagePullPolicy: "IfNotPresent"
          ports:
            - containerPort: 5432
          volumeMounts:
            - mountPath: /var/lib/postgresql/data
              name: postgres-vm
          resources:
            limits:
              memory: "2Gi"
              cpu: "1"
            requests:
              memory: "1Gi"
              cpu: "0.5"
      volumes:
        - name: postgres-vm
          persistentVolumeClaim:
            claimName: postgres

 

The oc apply command with the -f or --filename option can be used to create the stateful set using the template YAML file.

~]$ oc apply -f statefulset.yml 
statefulset/postgres created

 

The oc get statefulsets command will return the list of stateful sets. Since the stateful set YAML had replicas: 1 this is why 1 pod was created by the stateful set. In this example, since ready is 1/1 this means there should be a running postgres pod.

]$ oc get statefulsets
NAME       READY   AGE
postgres   1/1     27m

 

And the oc get pods command can be used to ensure the postgres pod is running.

$ oc get pods
NAME                             READY   STATUS    RESTARTS   AGE
postgres-0                       1/1     Running   0          29m

 

And the pod logs should return something like this.

]$ oc logs pod/postgres-0
The files belonging to this database system will be owned by user "postgres".
This user must also own the server process.

The database cluster will be initialized with locale "en_US.utf8".
The default database encoding has accordingly been set to "UTF8".
The default text search configuration will be set to "english".

Data page checksums are disabled.

fixing permissions on existing directory /var/lib/pgsql/data/userdata ... ok
creating subdirectories ... ok
selecting dynamic shared memory implementation ... posix
selecting default max_connections ... 100
selecting default shared_buffers ... 128MB
selecting default time zone ... UTC
creating configuration files ... ok
running bootstrap script ... ok
performing post-bootstrap initialization ... ok
syncing data to disk ... ok

initdb: warning: enabling "trust" authentication for local connections
initdb: hint: You can change this by editing pg_hba.conf or using the option -A, or --auth-local and --auth-host, the next time you run initdb.

Success. You can now start the database server using:

    pg_ctl -D /var/lib/pgsql/data/userdata -l logfile start

waiting for server to start....2025-10-09 01:15:43.003 UTC [35] LOG:  redirecting log output to logging collector process
2025-10-09 01:15:43.003 UTC [35] HINT:  Future log output will appear in directory "log".
 done
server started
/var/run/postgresql:5432 - accepting connections
=> sourcing /usr/share/container-scripts/postgresql/start/set_passwords.sh ...
ALTER ROLE
waiting for server to shut down.... done
server stopped
Starting server...
2025-10-09 01:15:43.244 UTC [1] LOG:  redirecting log output to logging collector process
2025-10-09 01:15:43.244 UTC [1] HINT:  Future log output will appear in directory "log".

 




Did you find this article helpful?

If so, consider buying me a coffee over at Buy Me A Coffee



Comments


Add a Comment


Please enter 15052a in the box below so that we can be sure you are a human.