Skip to main content

Command Palette

Search for a command to run...

DB Containers: Deploying Oracle Databases on RedHat OpenShift was a breeze

Success is subject to availability, please read fine print to complete details

Published
19 min read
DB Containers: Deploying Oracle Databases on RedHat OpenShift was a breeze
S

I work with software but it does not define me and my constant is change and I live a life of evolution. Learning, adapting, forgetting, re-learning, repeating I am not a Developer, I simply use software tools to solve interesting challenges and implement different use cases.

Just of out curiosity I reached out to the RedHat team in Switzerland to test how well the Oracle Database Operator for Kubernetes (DB Operator or OraOperator) functions on a Non-Oracle environment like OpenShift.

The main objectives to achieve were the installation and Single Instance Database deployment. In general the process on Oracle Cloud Infrastructure is simple and mostly pain free and after my initial tests, I can confirm the same on OpenShift.

About this Post

This post will focus on the high level steps used to deploy single database instance using the Oracle DB Operator, what to look out for and any major challenges that were faced.

The DB Operator lists the following Kubernetes variants that have been tested:

My colleagues and I have cut our teeth on OKE but for someone working at Oracle using Oracle Cloud, it should work … Right?!

💡
It Does :)

The Objectives are Simple

  1. Install the DB Operator on OpenShift

  2. Deploy a Database with it

  3. Make some observations

    1. How easy was it to install?

    2. Any points to note, work-arounds or gotchas?

    3. Note any issues & solutions (if available)

Keeping this one very basic and simple. I’m no kubectl expert and feeling pretty good that I can even check the status of the containers, so nothing fancy here.

About the Environment

I’d like to thank the RedHat Marketing team Switzerland (Romy Lienhard) and partner [TD SYNNEX], Artem Zwahlen and Shaun Wetli, for making this possible and my partner in crime Victor Vendo who has been supporting me on this journey.

The OpenShit cluster was hosted by the Solution Center with enough resources to deploy a personal data center.

Techie Details that Matter

Infrastructure ProviderVSphere
OpenShift version4.19.16
DB Operator version2.0.0
Storage Class: Provisionercsi.vsphere.vmware.com
Storage Class: Classthin-csi

Getting the DB Operator

The Oracle Database Operatorwas not listed in the RedHat OpenShift OperatorHub in the Cluster, but in a recent announcement it can be found their Software Catalog.

So for now, manual install using the provided YAML from the GitHub repo or using one of the options on the OperatorHub.io

💡
The OperatorHub does not provide any details on the pre-requisites, supported environments, permissions or roles. It is recommended to use the instructions from the main repo: https://github.com/oracle/oracle-database-operator

Check the Prerequisites and follow the child sections for the lifecycle operations that you will perform on the cluster. (Single Instance Database Prerequisites)

With this details, lets get to it.

To Install the DB Operator - follow the Repo Instructions

Before we get into it there are very few OpenShift specific prerequisites or steps, which is good for us.

Secondly, I recommend cloning the repository vs. copy pasting YAML from the repo one by one.

As I mentioned above, follow the instructions in the GitHub repo, they are fairly straight forward but apply some common sense.

For example: A Certificate Manager is required as the operator uses WebHooks. If your environment already as one deployed, do not install another one.

💡
The repo specifies v1.16.2 cert-manager, I would recommend to go directly to https://github.com/cert-manager/cert-manager/releases and use the latest released version

Certificate Manager Installed - Check ✅

💡
For Real world deployments with security constraints and restrictions, ensure the required operation permissions & role bindings, follow your companies practices. Any Questions can be raised as Issues for additional details.

Applying YAML - kubectl or oc

OpenShift provides a version of the kubectl cli tool called OpenShift CLI or oc for short. The 3.11 documentation clarifies the difference best but in a nutsheell oc offers the same capabilities as the kubectl, but it is extended to natively support OpenShift Container Platform features.

I would use oc when possible and the main syntax for basic commands are the same:

# Kubectl samples
kubectl get all -n sidb-ns
kubectl get singleinstancedatabases -n sidb-ns
kubectl get services -n sidb-ns
kubectl apply -f sidb_create_dbfree26ai.yaml

# oc samples = save on keyboard strokes
oc get all -n sidb-ns
oc get singleinstancedatabases -n sidb-ns
oc get services -n sidb-ns
oc apply -f sidb_create_dbfree26ai.yaml

Role Bindings

We selected the Cluster Scoped deployment option, but I am sure as a best practice, limiting the operators deployment and monitoring scope is better.

💡
This section in the documentation is an either or for the Role Bindings, followed by the Cluster Role and Cluster Role Bindings for NodePort services. Don’t miss this section if needed.

Single Instance Database Prerequisites

You may opt to proceed with the Single Instance Database Prerequisites now to conclude all of these steps together.

The optional response role bindings are recommended for database deployments for node access, storage and volume management.

This is also one of those few OpenShift specific perquisites as OpenShift requires additional Security Context Constraints (SCC) for deploying and managing the SingleInstanceDatabase resource.

Review the openshift_rbac.yaml and make note of the objects and resources that are created.

💡
The sample YAML binds the Security Context Constraints to the service account sidb-sa in namespace sidb-ns. You may update this appropriately to suite your environment.
💡
If you copy the command text to apply the OpenShift Security Constraints, please note that currently there is a type in the file name. openshift-rbac.yaml but it should be with and underscore: openshift_rbac.yaml

Install the DB Operator itself

Apply the oracle-database-operator.yaml to deploy the operator and check that the pods are up and running.

# Use oc to install the operator
oc apply -f oracle-database-operator.yaml

# Check the pods in the oracle-database-operator-system namespace
oc get pods -n oracle-database-operator-system
NAME                                                           READY   STATUS    RESTARTS   AGE
oracle-database-operator-controller-manager-59f6bd9b64-65zq2   1/1     Running   0          2m
oracle-database-operator-controller-manager-59f6bd9b64-dvxnf   1/1     Running   0          2m
oracle-database-operator-controller-manager-59f6bd9b64-x7ljk   1/1     Running   0          2m

Creating an Oracle Single Instance Database

The Operator does provide lifecycle management for different database infrastructure options:

Following the Containerized Oracle Single Instance Database and Data Guard, completing the prerequisites, it is simple to Create a Database

💡
Set the serviceAccountName attribute to sidb-sa and the namespace to sidb-ns or the ones specified in the openshift_rbac.yaml) in the resource YAML file before deploying the SingleInstanceDatabase resource.

Copy the Create Template

Create a copy of the config/samples/sidb/singleinstancedatabase_create.yaml, edit it for the type of database container to deploy.

Samples are provided for (not a complete list):

SampleDatabase Version
Complete Templatesingleinstancedatabase.yaml
Enterprise Edition v19csingleinstancedatabase_create.yaml
Free Edition v236aisingleinstancedatabase_free.yaml
Free Edition Lite v236aisingleinstancedatabase_free-lite.yaml
Free Edition True Cache v236aisingleinstancedatabase_free-truecache.yaml
Oracle Rest Data Serviceoraclerestdataservice.yaml

Edit the Template

Update the template appropriately

  • Name of the resource

  • Ensure the Namespace is correct

  • Specify the resources setting and configuration for deployment

  • Update the Storage Class to match the available Storage Classes available in your cluster

💡
If deploying on OpenShift, change service account name to 'sidb-sa' or the one specified in the opensift-rbac.yaml file

Add / verify the service account name to run the deployment as. This goes in the spec section

Sample DB Free

#
# Copyright (c) 2023, Oracle and/or its affiliates.
# Licensed under the Universal Permissive License v 1.0 as shown at http://oss.oracle.com/licenses/upl.
#

apiVersion: database.oracle.com/v4
kind: SingleInstanceDatabase
metadata:
  name: db001
  namespace: sidb-ns

spec:

  ## DB edition
  edition: free

  ## Secret containing SIDB password mapped to secretKey
  adminPassword:
    secretName: db-admin-secret

  ## Database image details
  image:
    ## Oracle Database Free is only supported from DB version 23.2 onwards
    pullFrom: container-registry.oracle.com/database/free:23.7.0.0
    pullSecrets: ocr-snurse-cred
    prebuiltDB: true

  ## size is the required minimum size of the persistent volume
  ## storageClass is specified for automatic volume provisioning
  ## accessMode can only accept one of ReadWriteOnce, ReadWriteMany
  persistence:
    size: 50Gi
    ## Update as appropriate for other cloud service providers
    storageClass: "thin-csi"
    accessMode: "ReadWriteOnce"

  ## Count of Database Pods. Should be 1 for free edition.
  replicas: 1

  ## Service Account
  serviceAccountName: "sidb-sa"

The file is saved as create_sidb_23ai_db001.yaml

Create Your Secrets

The YAML file to create the single instance database references two secrets. The database admin password and the pull secret.

  • spec.adminPassword.secretName: db-admin-secret

  • image.pullSecrets: ocr-snurse-cred

Create each of these in your project / namespace

# Create the Admin Password Secret
oc create secret generic db-admin-secret \
 --from-literal=oracle_pwd=<specify password here> \
 -n sidb-ns

# response
secret/db-admin-secret created 

# Create the Pull Secret used to connect to the Container REgistry
$ oc create secret docker-registry oracle-container-registry-secret \
 --docker-server=container-registry.oracle.com \
 --docker-username='<oracle-sso-email-address>' \
 --docker-password='<container-registry-auth-token>' \
 --docker-email='<oracle-sso-email-address>' \
 -n sidb-ns

# reponse
secret/oracle-container-registry-secret created
💡
The supplied commands in the documentation do not include the namespace option. If you run all of these steps in sequence after creating the project, then the default context namespace for all statements will be in that project / namespace. I add them for consistency and because I Trust No One!

Deploy the Image

The deployment is the simplest of steps, requiring a single kubectl / oc command.

# Apply the template to deploy the image as a pod
oc apply -f create_sidb_23ai_db001.yaml

# response
singleinstancedatabase.database.oracle.com/sidb-sample created

This message sounds great but to be sure verify that the new pod is created and starts the initialisation process. This will include downloading the container image and applying the setup scripts to stand up the database.

This may take some time, usually 5 minutes from Init to Running and the database services to be Ready.

Monitor the Image Deployment

Use the OpenShift UI to monitor the progress or standard kubectl options such as describe or logs.

Using cli is easy and standard across Kubernetes platforms. First use the get to list all pods or all resources in the namespace.

# Get All Pods in the namespace
oc get pods -n sidb-ns

# response
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS     RESTARTS   AGE
pod/db001-r3qq4   0/1     Init:0/1   0          7s

# Get All Resources in the namespace
oc get all -n sidb-ns

# response once the Pod is running with Get All
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS    RESTARTS   AGE
pod/db001-r3qq4   0/1     Running   0          3m9s

NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        3m9s
db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   3m9s

othing is shown - Time to Troubleshoot!

If nothing is returned, no pods are listed or the status shows Init:CrashLoopBackOff, check the Operator System logs.

If the default YAML was used to install the Operator, then 3 Operator Pods will have been deployed.

  1.    # Tail the Operator logs from the Pods
      oc logs -f -n oracle-database-operator-system \
       $(oc get pod -o name -n oracle-database-operator-system|tail -1)
    
      # Full output is not shown
      ....
      2025-12-02T10:29:37Z    INFO    singleinstancedatabase-resource validate create {"name": "db001"}
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Reconcile requested
      2025-12-02T10:29:37Z    INFO    singleinstancedatabase-resource validate update {"name": "db001"}
      2025-12-02T10:29:37Z    INFO    singleinstancedatabase-resource validate create {"name": "db001"}
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Entering reconcile validation
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Completed reconcile validation
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Creating a new PVC      {"createPVC Datafiles-Vol": {"name":"db001","namespace":"sidb-ns"}, "PVC.Namespace": "sidb-ns", "PVC.Name": "db001"}
      2025-12-02T10:29:37Z    INFO    No db001 Pod is Ready  {"controller": "singleinstancedatabase", "controllerGroup": "database.oracle.com", "controllerKind": "SingleInstanceDatabase", "SingleInstanceDatabase": {"name":"db001","namespace":"sidb-ns"}, "namespace": "sidb-ns", "name": "db001", "reconcileID": "ac6a76bb-31e7-4e70-9ef5-feae6370378b", "FindPods": {"name":"db001","namespace":"sidb-ns"}}
      2025-12-02T10:29:37Z    INFO    db001 Pods Available ( Other Than Ready Pod )  {"controller": "singleinstancedatabase", "controllerGroup": "database.oracle.com", "controllerKind": "SingleInstanceDatabase", "SingleInstanceDatabase": {"name":"db001","namespace":"sidb-ns"}, "namespace": "sidb-ns", "name": "db001", "reconcileID": "ac6a76bb-31e7-4e70-9ef5-feae6370378b", "FindPods": {"name":"db001","namespace":"sidb-ns"}, " Names :": []}
      2025-12-02T10:29:37Z    INFO    Total No Of db001 PODS {"controller": "singleinstancedatabase", "controllerGroup": "database.oracle.com", "controllerKind": "SingleInstanceDatabase", "SingleInstanceDatabase": {"name":"db001","namespace":"sidb-ns"}, "namespace": "sidb-ns", "name": "db001", "reconcileID": "ac6a76bb-31e7-4e70-9ef5-feae6370378b", "FindPods": {"name":"db001","namespace":"sidb-ns"}, "Count": 0}
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Replica Info    {"createPods": {"name":"db001","namespace":"sidb-ns"}, "Found": 0, "Required": 1}
      2025-12-02T10:29:37Z    INFO    controllers.database.SingleInstanceDatabase     Creating a new db001 POD       {"createPods": {"name":"db001","namespace":"sidb-ns"}, "POD.Namespace": "sidb-ns", "POD.Name": "db001-r3qq4"}
      2025-12-02T10:29:37Z    ERROR   controllers.database.SingleInstanceDatabase     Failed to create new db001 POD {"createPods": {"name":"db001","namespace":"sidb-ns"}, "pod.Namespace":
      ....
    

Hopefully an indication of the root cause will help identify the problem. At this stage, we’ve only completed a few prerequisite tasks, so re-check them before proceeding.

IF(All is Good)

To continue monitoring the pods progress using the describe or viewing the logs. The describe should return even if the pod is initialising but the logs will only return when the pod is Running.

# Using Describe to get Pod details
oc describe -n sidb-ns pod/db001-r3qq4

# response
Name:             db001-iyge6
Namespace:        sidb-ns
Priority:         0
Service Account:  sidb-sa
Node:             ocp-oracle-xbvj4-worker-0-kq5g9/192.168.230.202
Start Time:       Thu, 04 Dec 2025 10:17:03 +0000
Labels:           app=db003
                  version=
Annotations:      k8s.ovn.org/pod-networks:
                    {"default":{"ip_addresses":["10.128.2.28/23"],"mac_address":"0a:58:0a:80:02:1c","gateway_ips":["10.128.2.1"],"routes":[{"dest":"10.128.0.0...
                  k8s.v1.cni.cncf.io/network-status:
                    [{
                        "name": "ovn-kubernetes",
                        "interface": "eth0",
                        "ips": [
                            "10.128.2.28"
                        ],
                        "mac": "0a:58:0a:80:02:1c",
                        "default": true,
                        "dns": {}
                    }]
                  openshift.io/scc: sidb-oracle-user-scc
Status:           Running
IP:               10.128.2.28
IPs:
  IP:           10.128.2.28
Controlled By:  SingleInstanceDatabase/db001
Init Containers:
  init-prebuiltdb:
    Container ID:  cri-o://1f8b03f21098142b3bf12ea331656e046c10ca7d7fae9762dbd246b8043739d4
    Image:         container-registry.oracle.com/database/free:23.7.0.0
    Image ID:      container-registry.oracle.com/database/free@sha256:229c2015c60b4ee996bd1643576d189810a4e8d1fde772f80fdc057a2d5deb96
    Port:          <none>
    Host Port:     <none>
    Command:
      /bin/sh
      -c
      if [ ! -d /mnt/oradata/${ORACLE_SID} -a -d $ORACLE_BASE/oradata/${ORACLE_SID} ]; then cp -v $ORACLE_BASE/oradata/.${ORACLE_SID}$CHECKPOINT_FILE_EXTN /mnt/oradata &&  cp -vr $ORACLE_BASE/oradata/${ORACLE_SID} /mnt/oradata && cp -vr $ORACLE_BASE/oradata/dbconfig /mnt/oradata; fi 
    State:          Terminated
      Reason:       Completed
      Exit Code:    0
      Started:      Thu, 04 Dec 2025 10:20:28 +0000
      Finished:     Thu, 04 Dec 2025 10:20:34 +0000
    Ready:          True
    Restart Count:  0
    Environment:
      ORACLE_SID:  FREE
    Mounts:
      /mnt/oradata from datafiles-vol (rw)
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-frj8w (ro)
Containers:
  db001:
    Container ID:   cri-o://af137efc46e563e49474ee85ba850d863281f1d03f70b683964c654c93808677
    Image:          container-registry.oracle.com/database/free:23.7.0.0
    Image ID:       container-registry.oracle.com/database/free@sha256:229c2015c60b4ee996bd1643576d189810a4e8d1fde772f80fdc057a2d5deb96
    Ports:          1521/TCP, 5500/TCP
    Host Ports:     0/TCP, 0/TCP
    State:          Running
      Started:      Thu, 04 Dec 2025 10:20:35 +0000
    Ready:          True
    Restart Count:  0
    Readiness:      exec [/bin/sh -c if [ -f $ORACLE_BASE/checkDBLockStatus.sh ]; then $ORACLE_BASE/checkDBLockStatus.sh ; else $ORACLE_BASE/checkDBStatus.sh; fi ] delay=20s timeout=20s period=60s #success=1 #failure=3
    Environment:
      SVC_HOST:             db003
      SVC_PORT:             1521
      ORACLE_CHARACTERSET:  
      ORACLE_EDITION:       free
    Mounts:
      /opt/oracle/oradata from datafiles-vol (rw)
      /run/secrets/oracle_pwd from oracle-pwd-vol (ro,path="oracle_pwd")
      /var/run/secrets/kubernetes.io/serviceaccount from kube-api-access-frj8w (ro)
Conditions:
  Type                        Status
  PodReadyToStartContainers   True 
  Initialized                 True 
  Ready                       True 
  ContainersReady             True 
  PodScheduled                True 
Volumes:
  datafiles-vol:
    Type:       PersistentVolumeClaim (a reference to a PersistentVolumeClaim in the same namespace)
    ClaimName:  db001
    ReadOnly:   false
  oracle-pwd-vol:
    Type:        Secret (a volume populated by a Secret)
    SecretName:  db-admin-secret
    Optional:    false
  tls-secret-vol:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>
  custom-scripts-vol:
    Type:       EmptyDir (a temporary directory that shares a pod's lifetime)
    Medium:     
    SizeLimit:  <unset>
  kube-api-access-frj8w:
    Type:                    Projected (a volume that contains injected data from multiple sources)
    TokenExpirationSeconds:  3607
    ConfigMapName:           kube-root-ca.crt
    ConfigMapOptional:       <nil>
    DownwardAPI:             true
    ConfigMapName:           openshift-service-ca.crt
    ConfigMapOptional:       <nil>
QoS Class:                   BestEffort
Node-Selectors:              <none>
Tolerations:                 node.kubernetes.io/not-ready:NoExecute op=Exists for 300s
                             node.kubernetes.io/unreachable:NoExecute op=Exists for 300s
Events:                      <none>

This shows the deployment details of the pod but not the database state. The operator provides the custom resource definition singleinstancedatabase, along with other definitions, that can be managed.

Viewing the Single Instance Database status

# Get the Status of the single instance database 
oc get singleinstancedatabase db001 -n sidb-ns

# response
NAME    EDITION   STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free      Healthy   PRIMARY   23.7.0.25.01   192.168.230.198:32193/FREE   Unavailable        Unavailable

The Status progresses during the provisioning, startup, ready or failure states. Healthy is up and Running but to be sure, we can view the pods logs

# View the pod logs
oc logs -f pod/db001-r3qq4   

# Snippets of the Response is shown           
Starting Oracle Net Listener.
Oracle Net Listener started.
Starting Oracle Database instance FREE.
Oracle Database instance FREE started.
The Oracle base remains unchanged with value /opt/oracle
SQL*Plus: Release 23.0.0.0.0 - Production on Thu Dec 4 10:20:39 2025
Version 23.7.0.25.01
Copyright (c) 1982, 2025, Oracle. All rights reserved.
Connected to:
Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.7.0.25.01
SQL>
User altered.
SQL>
User altered.
SQL>
Session altered.
SQL>
User altered.
SQL> Disconnected from Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.7.0.25.01
The Oracle base remains unchanged with value /opt/oracle
#########################
DATABASE IS READY TO USE!
#########################
The following output is now a tail of the alert.log:

...

The log will update as the database is initialised and configured. When the “DATABASE IS READY TO USE!” appears, then it is ready to be connected to by applications and SQL clients.

Of course this is much simpler in the OpenShift Console.

  • Navigate to Home → Projects

  • Search for the Project / Namespace Name

  • Select the project

  • Navigate to Pods in the Inventory Section

  • Select the POD

  • Navigate to the Logs tab

You can also use this to access the pods terminal.

Connecting to the Database Instance

Connecting to a pods running in a Kubernetes cluster are not covered in this post. The pods can be accessed directly, via port forwarding on a jump host or load balancer.

For connectivity using port forwarding or a load balancer, the Cluster IPs and Ports for the services are obtained

# Get Services Info for connections outside of the cluster
oc get services -n sidb-ns

# response
NAME        TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        15m
db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   15m

Connectivity from the same subnet or that has routing to the cluster resources can use a direct connection.

# Get Single Instance Database Info for Direct connections with the same subnet as the POD
oc get singleinstancedatabase db001 -n sidb-ns

# response
NAME    EDITION   STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free      Healthy   PRIMARY   23.7.0.25.01   192.168.230.198:32193/FREE   Unavailable        Unavailable
💡
Note that not only are the IP addresses different but also the Listener Ports as well

The client I use to connect is in the same subnet, so I opt for the direct connection. Using the Oracle SQL Developer extension in VS Code, I create a connection to the CDB$ROOT and PDB containers (FREE & FREEPDB1 respectively).

You are all set to start you DB & Application Development on Oracle Databases.

💡
In the post I used the DB Free Edition which has pre-set limits and restrictions on deployment and usage but is free. Other containers will require a valid license agreement with Oracle, do not violate the license usage or policy agreements.

Removing an Oracle Single Database Instance

Luckily for those who’ve worked in a Kubernetes or OpenShift environment this step is no different that removing or deleting other deployed resources.

# Using normal oc (kubectl) to delete a resource
## Listing existing resources
oc get all -n sidb-ns  

#response   
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS    RESTARTS   AGE
pod/db001-37gsz   1/1     Running   0          3d15h
pod/db002-lhwg6   1/1     Running   0          3d15h
pod/db003-2m0jl   1/1     Running   0          3d15h

NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
service/db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        3d21h
service/db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   3d21h
service/db002       ClusterIP   172.30.190.26    <none>        1521/TCP                        7d2h
service/db002-ext   NodePort    172.30.24.17     <none>        5500:31356/TCP,1521:30281/TCP   7d2h
service/db003       ClusterIP   172.30.33.212    <none>        1521/TCP                        5d2h
service/db003-ext   NodePort    172.30.179.29    <none>        5500:32660/TCP,1521:31614/TCP   5d2h

## Listing the single instance databases
oc get singleinstancedatabase -n sidb-ns 

# reponse
NAME    EDITION       STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free          Healthy   PRIMARY   23.7.0.25.01   192.168.230.199:32193/FREE   Unavailable        Unavailable
db002   Enterprise    Healthy   PRIMARY   19.26.0.0.0   192.168.230.200:30281/ORCL    Unavailable        https://192.168.230.199:32652/em
db003   Free          Healthy   PRIMARY   23.7.0.25.01   192.168.230.199:31614/FREE   Unavailable        Unavailable

## Deleting the single database instance "db003"
oc delete -f sidb-db003.yaml 

# response
singleinstancedatabase.database.oracle.com "db003" deleted

## Listing existing resources
oc get all -n sidb-ns  

#response   
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS    RESTARTS   AGE
pod/db001-37gsz   1/1     Running   0          3d15h
pod/db002-lhwg6   1/1     Running   0          3d15h

NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
service/db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        3d21h
service/db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   3d21h
service/db002       ClusterIP   172.30.190.26    <none>        1521/TCP                        7d2h
service/db002-ext   NodePort    172.30.24.17     <none>        5500:31356/TCP,1521:30281/TCP   7d2h

## Listing the single instance databases
oc get singleinstancedatabase -n sidb-ns 

# reponse
NAME    EDITION       STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free          Healthy   PRIMARY   23.7.0.25.01   192.168.230.199:32193/FREE   Unavailable        Unavailable
db002   Enterprise    Healthy   PRIMARY   19.26.0.0.0   192.168.230.200:30281/ORCL    Unavailable        https://192.168.230.199:32652/em

The Single Instance Database, its services and pod are all removed.

The operator does provide an additional option to clean up instances and associated resources like the pod or services.

# Using the Operator Signle Instance Database CRD operations
## Listing existing resources
oc get all -n sidb-ns  

#response   
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS    RESTARTS   AGE
pod/db001-37gsz   1/1     Running   0          3d15h
pod/db002-lhwg6   1/1     Running   0          3d15h

NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
service/db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        3d21h
service/db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   3d21h
service/db002       ClusterIP   172.30.190.26    <none>        1521/TCP                        7d2h
service/db002-ext   NodePort    172.30.24.17     <none>        5500:31356/TCP,1521:30281/TCP   7d2h

## Listing the single instance databases
oc get singleinstancedatabase -n sidb-ns 

# reponse
NAME    EDITION       STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free          Healthy   PRIMARY   23.7.0.25.01   192.168.230.199:32193/FREE   Unavailable        Unavailable
db002   Enterprise    Healthy   PRIMARY   19.26.0.0.0   192.168.230.200:30281/ORCL    Unavailable        https://192.168.230.199:32652/em

## Deleting the single instance database "db002"
oc delete singleinstancedatabase db002 -n sidb-ns

# response
singleinstancedatabase.database.oracle.com "db002" deleted

## Listing the single instance databases
oc get all -n sidb-ns
# reponse
Warning: apps.openshift.io/v1 DeploymentConfig is deprecated in v4.14+, unavailable in v4.10000+
NAME              READY   STATUS    RESTARTS   AGE
pod/db001-37gsz   1/1     Running   0          3d15h

NAME                TYPE        CLUSTER-IP       EXTERNAL-IP   PORT(S)                         AGE
service/db001       ClusterIP   172.30.211.151   <none>        1521/TCP                        3d21h
service/db001-ext   NodePort    172.30.145.198   <none>        5500:32652/TCP,1521:32193/TCP   3d21h

## Listing the single instance databases
oc get singleinstancedatabase -n sidb-ns 

# reponse     
NAME    EDITION   STATUS    ROLE      VERSION        CONNECT STR                  TCPS CONNECT STR   OEM EXPRESS URL
db001   Free      Healthy   PRIMARY   23.7.0.25.01   192.168.230.199:32193/FREE   Unavailable        Unavailable

Each command option provides the same results and of course this can be completed via the console.

Removing Autonomous Databases

Although not a focus of this post, I just wanted to note that the deletion of Autonomous Databases (ADBs) that bounded to or created in Oracle Cloud Infrastructure (OCI) supports two deletion operations.

  1. Unbind

    In this case the ADB remains deployed in OCI

  2. Delete

    The hardLink property which defines the behavior when the resource is deleted from the cluster. If the hardLink is set to true, the Operator terminates the Autonomous Database in OCI when the resource is removed; otherwise, the database remains unchanged (same as UNBIND).

Removing the resource can be performed using the oc/kubectl delete and referencing the resource YAM file or the autonomousdatabase.database.oracle.com or for short adb CRD

Samples

# Listing the Bounded Autonomous Databases
## Option 1
kubectl get adbs
NAME           DISPLAY NAME   DB NAME        STATE       DEDICATED   OCPUS   STORAGE (TB)   WORKLOAD TYPE   CREATED
snapexdb23ai   snapexdb23ai   snapexdb23ai   AVAILABLE   false       0       1              OLTP            2024-09-19 08:21:43 UTC
snodbodb23ai   snodbodb23ai   snodbodb23ai   STOPPED     false       0       1              OLTP            2025-07-11 08:06:47 UTC

## Option 2
kubectl get autonomousdatabase.database.oracle.com
NAME           DISPLAY NAME   DB NAME        STATE       DEDICATED   OCPUS   STORAGE (TB)   WORKLOAD TYPE   CREATED
snapexdb23ai   snapexdb23ai   snapexdb23ai   AVAILABLE   false       0       1              OLTP            2024-09-19 08:21:43 UTC
snodbodb23ai   snodbodb23ai   snodbodb23ai   STOPPED     false       0       1              OLTP            2025-07-11 08:06:47 UTC

## Delete or Unbind the resource fromt he cluster
kubectl delete adb/snodbodb23ai

# response
autonomousdatabase.database.oracle.com/snodbodb23ai deleted

To clean up the ADB instance in OCI please refer to the Delete the resource from the cluster section of the Operator documentation.

Conclusion

As expected the process was straight forward and simple. It did not differ much from other Kubernetes environment with only a few OpenShift caveats.

Using oc vs kubectl was an easy switch and OpenShift provides developer and user enhancements that simply context switching.

I look forward to the day when this operator is available in the cluster’s Operator Hub, which would simplify the deployment even more.

If you are familiar with Kubernetes and/or OpenShift, then this will only be a high level intro to using the Operator to deploy different database options. In future testing, I hope to explore the OCI Database deployments such as Autonomous provisioning.

All of the Operator operations are supported but if any issues arise issues can be posted to the GitHub repository. So far, so good, nothing to report except for a minor typo in the documentation.

Are you thinking about deploying containerised Oracle Databases for Development or Testing on OpenShift?

If so, what has your experience been like?