Find the Best Cosmetic Hospitals

Explore trusted cosmetic hospitals and make a confident choice for your transformation.

“Invest in yourself — your confidence is always worth it.”

Explore Cosmetic Hospitals

Start your journey today — compare options in one place.

OpenShift Lab 8: Work with Databases in OpenShift Using the oc CLI Tool

Lab Objective

In this lab, you will deploy a PostgreSQL database in OpenShift using the oc CLI tool. You will learn how to:

  • Create a new OpenShift project.
  • Store database credentials in a Kubernetes Secret.
  • Create persistent storage for database data.
  • Deploy PostgreSQL using a Deployment.
  • Expose PostgreSQL internally using a Service.
  • Connect to the database from another pod inside the same project.
  • Create a table, insert data, and query data.
  • Troubleshoot common database deployment issues.
  • Clean up all lab resources.

Important Concept

A database should normally not be exposed to the public internet using an OpenShift Route.

In this lab:

  • PostgreSQL runs inside the OpenShift cluster.
  • Other pods connect to PostgreSQL using the internal Service name.
  • The database Service is called postgresql.
  • Applications inside the same project can connect to it using:
postgresql:5432
Code language: CSS (css)

Lab Architecture

Student Terminal
      |
      | oc CLI
      v
OpenShift Project: lab8-database-cli
      |
      +-- Secret: postgresql-secret
      |
      +-- PVC: postgresql-data
      |
      +-- Deployment: postgresql
      |       |
      |       +-- Pod: PostgreSQL database
      |
      +-- Service: postgresql
      |
      +-- Pod: pg-client
              |
              +-- connects to postgresql:5432

Prerequisites

Before starting this lab, students should have:

  • Access to an OpenShift cluster.
  • The oc CLI installed.
  • Permission to create a project.
  • Permission to create pods, deployments, services, secrets, and PVCs.
  • A working internet connection from the cluster to pull the container image.

This lab works best on:

  • OpenShift Local / CRC
  • OpenShift Developer Sandbox
  • OpenShift Container Platform 4.x
  • OKD 4.x

Database Used in This Lab

ItemValue
DatabasePostgreSQL
VersionPostgreSQL 15
Database imagequay.io/sclorg/postgresql-15-c9s:latest
Database service namepostgresql
Database port5432
Database userstudent
Database passwordstudentpass123
Database namestudentdb

Step 1: Verify oc CLI

Run:

oc version

Expected output:

Client Version: ...
Server Version: ...
Kubernetes Version: ...

If you only see the client version and no server version, you are not logged in yet.

Step 2: Log in to OpenShift

For OpenShift Local / CRC, first get credentials:

crc console --credentials
Code language: JavaScript (javascript)

Then log in using the command shown by CRC, for example:

oc login https://api.crc.testing:6443 -u developer -p developer
Code language: JavaScript (javascript)

For OpenShift Developer Sandbox, copy the login command from the web console and paste it into your terminal. It usually looks like this:

oc login --token=<your-token> --server=<your-api-server>
Code language: HTML, XML (xml)

Verify login:

oc whoami

Expected output:

developer

or your OpenShift username.

Step 3: Create a New Project

Create a new project for this lab:

oc new-project lab8-database-cli
Code language: JavaScript (javascript)

Expected output:

Now using project "lab8-database-cli" on server ...
Code language: JavaScript (javascript)

Verify the current project:

oc project

Expected output:

Using project "lab8-database-cli" on server ...
Code language: JavaScript (javascript)

Step 4: Create a Secret for Database Credentials

Create a Secret to store the database username, password, and database name.

oc create secret generic postgresql-secret \
  --from-literal=POSTGRESQL_USER=student \
  --from-literal=POSTGRESQL_PASSWORD=studentpass123 \
  --from-literal=POSTGRESQL_DATABASE=studentdb
Code language: JavaScript (javascript)

Expected output:

secret/postgresql-secret created

Verify the Secret exists:

oc get secret postgresql-secret
Code language: JavaScript (javascript)

Expected output:

NAME                TYPE     DATA   AGE
postgresql-secret   Opaque   3      ...

Do not print secret values in a real production environment. This is a training lab, so we are using simple demo credentials.

Step 5: Create Persistent Storage for PostgreSQL

Create a PersistentVolumeClaim for database storage:

oc apply -f - <<'EOF'
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: postgresql-data
spec:
  accessModes:
    - ReadWriteOnce
  resources:
    requests:
      storage: 1Gi
EOF
Code language: JavaScript (javascript)

Expected output:

persistentvolumeclaim/postgresql-data created

Check the PVC:

oc get pvc postgresql-data
Code language: JavaScript (javascript)

Expected output when storage is available:

NAME              STATUS   VOLUME   CAPACITY   ACCESS MODES   STORAGECLASS   AGE
postgresql-data   Bound    ...      1Gi        RWO            ...            ...

If the status is Pending, your cluster may not have a default StorageClass or you may not have storage quota. Continue to the troubleshooting section and use the ephemeral fallback.

Step 6: Deploy PostgreSQL

Create the PostgreSQL Deployment:

oc apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
  labels:
    app: postgresql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
        - name: postgresql
          image: quay.io/sclorg/postgresql-15-c9s:latest
          imagePullPolicy: IfNotPresent
          ports:
            - name: postgresql
              containerPort: 5432
          envFrom:
            - secretRef:
                name: postgresql-secret
          volumeMounts:
            - name: postgresql-data
              mountPath: /var/lib/pgsql/data
      volumes:
        - name: postgresql-data
          persistentVolumeClaim:
            claimName: postgresql-data
EOF
Code language: JavaScript (javascript)

Expected output:

deployment.apps/postgresql created

Wait for the deployment to become ready:

oc rollout status deployment/postgresql --timeout=180s

Expected output:

deployment "postgresql" successfully rolled out
Code language: JavaScript (javascript)

Verify the pod:

oc get pods -l app=postgresql
Code language: JavaScript (javascript)

Expected output:

NAME                          READY   STATUS    RESTARTS   AGE
postgresql-xxxxxxxxxx-xxxxx   1/1     Running   0          ...

Step 7: Create an Internal Service for PostgreSQL

Create a Service so other pods can connect to PostgreSQL using a stable DNS name.

oc expose deployment/postgresql \
  --port=5432 \
  --target-port=5432 \
  --name=postgresql

Expected output:

service/postgresql exposed

Verify the Service:

oc get svc postgresql
Code language: JavaScript (javascript)

Expected output:

NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
postgresql   ClusterIP   ...             <none>        5432/TCP   ...
Code language: HTML, XML (xml)

Important: This Service is internal to the cluster. That is correct for a database.

Step 8: Check Database Logs

View PostgreSQL logs:

oc logs deployment/postgresql --tail=50

Expected output should include PostgreSQL startup messages.

Example output:

PostgreSQL init process complete; ready for start up.
database system is ready to accept connections

Exact log text can vary, but the important idea is that PostgreSQL should be ready to accept connections.

Step 9: Create a Temporary PostgreSQL Client Pod

Create a temporary client pod that includes the psql command-line tool.

oc run pg-client \
  --image=quay.io/sclorg/postgresql-15-c9s:latest \
  --restart=Never \
  -- sleep 3600

Expected output:

pod/pg-client created

Wait until the client pod is ready:

oc wait --for=condition=Ready pod/pg-client --timeout=120s

Expected output:

pod/pg-client condition met

Verify both pods:

oc get pods
Code language: JavaScript (javascript)

Expected output:

NAME                          READY   STATUS    RESTARTS   AGE
pg-client                     1/1     Running   0          ...
postgresql-xxxxxxxxxx-xxxxx   1/1     Running   0          ...

Step 10: Test Database Connectivity

From the client pod, connect to PostgreSQL through the Service name.

oc exec pg-client -- sh -lc \
  'PGPASSWORD=studentpass123 psql -h postgresql -U student -d studentdb -c "SELECT version();"'
Code language: JavaScript (javascript)

Expected output:

version
----------------------------------------------------------------------------------------------------
PostgreSQL 15 ...
(1 row)

This proves:

  • The PostgreSQL pod is running.
  • The Service name postgresql resolves inside the project.
  • The username and password work.
  • The database studentdb exists.

Step 11: Create a Table

Create a table named students.

oc exec pg-client -- sh -lc \
  'PGPASSWORD=studentpass123 psql -h postgresql -U student -d studentdb -c "CREATE TABLE IF NOT EXISTS students (id SERIAL PRIMARY KEY, name TEXT NOT NULL, city TEXT NOT NULL);"'
Code language: JavaScript (javascript)

Expected output:

CREATE TABLE

Step 12: Insert Sample Data

Insert three records:

oc exec pg-client -- sh -lc \
  'PGPASSWORD=studentpass123 psql -h postgresql -U student -d studentdb -c "INSERT INTO students (name, city) VALUES ('"'"'Rajesh'"'"', '"'"'Tokyo'"'"'), ('"'"'Asha'"'"', '"'"'Bengaluru'"'"'), ('"'"'Miguel'"'"', '"'"'Madrid'"'"');"'
Code language: JavaScript (javascript)

Expected output:

INSERT 0 3

Step 13: Query the Data

Query the table:

oc exec pg-client -- sh -lc \
  'PGPASSWORD=studentpass123 psql -h postgresql -U student -d studentdb -c "SELECT * FROM students;"'
Code language: JavaScript (javascript)

Expected output:

 id |  name  |   city
----+--------+-----------
  1 | Rajesh | Tokyo
  2 | Asha   | Bengaluru
  3 | Miguel | Madrid
(3 rows)

Step 14: Open an Interactive SQL Session

You can also open an interactive psql session:

oc exec -it pg-client -- sh

Inside the client pod, run:

export PGPASSWORD=studentpass123
psql -h postgresql -U student -d studentdb
Code language: JavaScript (javascript)

Inside psql, try:

\dt
SELECT * FROM students;
\q

Exit the pod shell:

exit
Code language: PHP (php)

Step 15: Inspect the Running Resources

Show all major resources in the project:

oc get all
Code language: JavaScript (javascript)

Expected resources include:

pod/pg-client
pod/postgresql-...
service/postgresql
deployment.apps/postgresql
replicaset.apps/postgresql-...

Show only database-related resources:

oc get deployment,svc,pvc,secret,pod -l app=postgresql
Code language: JavaScript (javascript)

Note: The Secret and PVC may not show with this label because we did not label them. You can list them directly:

oc get secret postgresql-secret
oc get pvc postgresql-data
Code language: JavaScript (javascript)

Describe the PostgreSQL Deployment:

oc describe deployment postgresql

Describe the PostgreSQL Service:

oc describe service postgresql

Step 16: Optional Local Port Forwarding

If your local machine has the psql client installed, you can forward PostgreSQL to your local machine.

Start port forwarding:

oc port-forward svc/postgresql 5432:5432

Expected output:

Forwarding from 127.0.0.1:5432 -> 5432
Forwarding from [::1]:5432 -> 5432
Code language: CSS (css)

Keep this terminal open.

In another terminal, connect locally:

PGPASSWORD=studentpass123 psql -h 127.0.0.1 -U student -d studentdb

Exit psql:

\q

Stop port forwarding with:

Ctrl+C

Step 17: Why We Do Not Create a Route for PostgreSQL

Do not run this for the database:

oc expose service/postgresql

A Route is normally used for HTTP/HTTPS web applications. PostgreSQL is a database service and should usually stay private inside the cluster.

Correct database access pattern:

Application Pod -> Service postgresql -> PostgreSQL Pod

Incorrect beginner pattern:

Internet -> Route -> PostgreSQL

Step 18: Do Not Scale This Database Deployment

Do not scale this PostgreSQL Deployment to multiple replicas:

oc scale deployment/postgresql --replicas=2

A single PostgreSQL database using one ReadWriteOnce volume should not be scaled like a stateless web application. For real high availability, use a database Operator or managed database service.

For this lab, keep:

oc scale deployment/postgresql --replicas=1

Expected output:

deployment.apps/postgresql scaled

Troubleshooting

Problem 1: PVC Stays Pending

Check the PVC:

oc get pvc postgresql-data
Code language: JavaScript (javascript)

If it shows:

STATUS: Pending
Code language: HTTP (http)

Possible reasons:

  • No default StorageClass.
  • No available persistent volumes.
  • Storage quota exceeded.

Check storage classes:

oc get storageclass
Code language: JavaScript (javascript)

If your training cluster does not support persistent storage, use the ephemeral fallback below.

Problem 2: Pod Shows ImagePullBackOff

Check pod status:

oc get pods
Code language: JavaScript (javascript)

Describe the pod:

oc describe pod -l app=postgresql

Possible reasons:

  • Cluster cannot pull from the image registry.
  • Network egress is blocked.
  • Registry is temporarily unavailable.

Problem 3: Pod Shows CrashLoopBackOff

Check logs:

oc logs deployment/postgresql --tail=100

Common causes:

  • Secret name is wrong.
  • Required environment variables are missing.
  • Storage mount has permission problems.

Problem 4: Client Cannot Resolve Hostname

If this command fails:

oc exec pg-client -- sh -lc \
  'PGPASSWORD=studentpass123 psql -h postgresql -U student -d studentdb -c "SELECT version();"'
Code language: JavaScript (javascript)

Check that the Service exists:

oc get svc postgresql
Code language: JavaScript (javascript)

Check that both pods are in the same project:

oc project
oc get pods
Code language: JavaScript (javascript)

Ephemeral Fallback: Use This Only If PVC Is Not Available

If your cluster does not provide persistent storage, delete the existing deployment and PVC:

oc delete deployment postgresql --ignore-not-found=true
oc delete pvc postgresql-data --ignore-not-found=true
Code language: JavaScript (javascript)

Create an ephemeral PostgreSQL deployment using emptyDir:

oc apply -f - <<'EOF'
apiVersion: apps/v1
kind: Deployment
metadata:
  name: postgresql
  labels:
    app: postgresql
spec:
  replicas: 1
  selector:
    matchLabels:
      app: postgresql
  template:
    metadata:
      labels:
        app: postgresql
    spec:
      containers:
        - name: postgresql
          image: quay.io/sclorg/postgresql-15-c9s:latest
          imagePullPolicy: IfNotPresent
          ports:
            - name: postgresql
              containerPort: 5432
          envFrom:
            - secretRef:
                name: postgresql-secret
          volumeMounts:
            - name: postgresql-data
              mountPath: /var/lib/pgsql/data
      volumes:
        - name: postgresql-data
          emptyDir: {}
EOF
Code language: JavaScript (javascript)

Wait for rollout:

oc rollout status deployment/postgresql --timeout=180s

If the Service already exists, keep it. If not, create it:

oc get svc postgresql || oc expose deployment/postgresql --port=5432 --target-port=5432 --name=postgresql
Code language: JavaScript (javascript)

Warning: With emptyDir, database data is lost when the pod is deleted or recreated. Use this only for temporary training labs.

Cleanup Option 1: Delete Only Lab Resources

Delete the client pod:

oc delete pod pg-client --ignore-not-found=true
Code language: JavaScript (javascript)

Delete the PostgreSQL Service:

oc delete service postgresql --ignore-not-found=true
Code language: JavaScript (javascript)

Delete the PostgreSQL Deployment:

oc delete deployment postgresql --ignore-not-found=true
Code language: JavaScript (javascript)

Delete the PVC:

oc delete pvc postgresql-data --ignore-not-found=true
Code language: JavaScript (javascript)

Delete the Secret:

oc delete secret postgresql-secret --ignore-not-found=true
Code language: JavaScript (javascript)

Verify cleanup:

oc get all
oc get pvc
oc get secret postgresql-secret
Code language: JavaScript (javascript)

Cleanup Option 2: Delete the Whole Project

If this project was created only for the lab, delete the whole project:

oc delete project lab8-database-cli
Code language: JavaScript (javascript)

Expected output:

project.project.openshift.io "lab8-database-cli" deleted
Code language: CSS (css)

Lab Review Questions

  1. Why did we store database credentials in a Secret instead of directly writing them in the Deployment?
  2. Why did we create a Service for PostgreSQL?
  3. Why did we not create an OpenShift Route for PostgreSQL?
  4. What is the difference between persistent storage and emptyDir?
  5. Why should this PostgreSQL Deployment not be scaled to multiple replicas?
  6. How would an application in the same project connect to this database?

Key Takeaways

  • Databases in OpenShift should normally be exposed internally with Services, not public Routes.
  • Secrets are used to store sensitive configuration such as database usernames and passwords.
  • PersistentVolumeClaims are used to request durable storage.
  • Pods should connect to databases using Service DNS names.
  • PostgreSQL listens on port 5432.
  • Stateful databases require different scaling and availability patterns than stateless web applications.

Find Trusted Cardiac Hospitals

Compare heart hospitals by city and services — all in one place.

Explore Hospitals
I’m a DevOps/SRE/DevSecOps/Cloud Expert passionate about sharing knowledge and experiences. I have worked at <a href="https://www.cotocus.com/">Cotocus</a>. I share tech blog at <a href="https://www.devopsschool.com/">DevOps School</a>, travel stories at <a href="https://www.holidaylandmark.com/">Holiday Landmark</a>, stock market tips at <a href="https://www.stocksmantra.in/">Stocks Mantra</a>, health and fitness guidance at <a href="https://www.mymedicplus.com/">My Medic Plus</a>, product reviews at <a href="https://www.truereviewnow.com/">TrueReviewNow</a> , and SEO strategies at <a href="https://www.wizbrand.com/">Wizbrand.</a> Do you want to learn <a href="https://www.quantumuting.com/">Quantum Computing</a>? <strong>Please find my social handles as below;</strong> <a href="https://www.rajeshkumar.xyz/">Rajesh Kumar Personal Website</a> <a href="https://www.youtube.com/TheDevOpsSchool">Rajesh Kumar at YOUTUBE</a> <a href="https://www.instagram.com/rajeshkumarin">Rajesh Kumar at INSTAGRAM</a> <a href="https://x.com/RajeshKumarIn">Rajesh Kumar at X</a> <a href="https://www.facebook.com/RajeshKumarLog">Rajesh Kumar at FACEBOOK</a> <a href="https://www.linkedin.com/in/rajeshkumarin/">Rajesh Kumar at LINKEDIN</a> <a href="https://www.wizbrand.com/rajeshkumar">Rajesh Kumar at WIZBRAND</a> <a href="https://www.rajeshkumar.xyz/dailylogs">Rajesh Kumar DailyLogs</a>

Related Posts

Best DevOps Tools in 2024

here’s a clear, structured breakdown of the Best DevOps Tools (grouped by categories), so you can use it for learning, training, or posts. 🚀 Best DevOps Tools…

Read More

OpenShift Tutorial – Deploy and Access Your First Applications using OpenShift Local

How to install oc? OpenShift: How to Install OpenShift CLI oc How to login? Login to the Openshift using Web Console and CLI using oc Copy the admin…

Read More

OpenShift Lab 14: Setting Up and Using OpenShift Serverless Functions on OpenShift Local

Lab Objective In this lab, you will install and use OpenShift Serverless Functions on OpenShift Local. You will create a simple Node.js serverless function, deploy it to…

Read More

OpenShift Lab 13: Deploy a Java Spring Boot Application with MySQL Using the OpenShift Web Console

Lab Objective In this lab, you will deploy a Java Spring Boot application on OpenShift using the OpenShift web console. You will deploy two components: The Java…

Read More

OpenShift Lab 12: Deploy an Application from an Existing Container Image Using the OpenShift Web Console

Lab Objective In this lab, you will deploy an application on OpenShift from an existing container image. This lab is different from building an application from source…

Read More

OpenShift: How to Install OpenShift CLI oc

Option – 1 – REDHAT Websites URL – https://access.redhat.com/downloads/content/290/ver=4.18/rhel—9/4.18.11/x86_64/product-software Option – 2 – OKD Websites The OKD (Origin Community Distribution of Kubernetes for OpenShift) is the open-source…

Read More
Subscribe
Notify of
guest
1 Comment
Newest
Oldest Most Voted
Skylar Bennett
Skylar Bennett
18 days ago

The tutorial explains database management using the oc CLI clearly, but it would be even more useful if it included considerations for running databases in production OpenShift environments. Areas such as storage provisioning, backup and restore planning, secret rotation, and handling database upgrades are essential for maintaining reliability. Additionally, incorporating guidance on performance monitoring and capacity planning would help teams identify potential issues before they affect application availability.

1
0
Would love your thoughts, please comment.x
()
x