⚙️ Enterprise IT DevOps

Oracle Database, APEX & ORDS on Kubernetes

DB Realm Inc. (Internal)

Challenge

Running Oracle Database on Kubernetes is officially supported and practically full of sharp edges. Storage access modes, operator-owned resources, rolling updates that cannot detach a volume, ORDS deployment topologies and APEX upgrade paths all fail in ways that generic Kubernetes experience does not prepare you for. Migrating a legacy VM-based Oracle estate into a cluster meant solving each of those in production, on databases that other work depended on.

Solution

Migrated a multi-version Oracle estate onto Kubernetes using Oracle's database operator, consolidating PDBs across 19c and 23ai instances with ORDS and APEX deployed alongside. Everything is declarative and reconciled by ArgoCD from a GitOps repository. The procedural knowledge is deliberately kept in a separate repository from the deployable state, so runbooks and failure modes are documented independently of the manifests that happen to be current.

Results

  • Multi-version Oracle estate running on Kubernetes
  • Declarative manifests reconciled by ArgoCD
  • Documented runbooks separated from deployable state
  • Failure modes captured as greppable error strings
Kubernetes Oracle Operator ORDS APEX GitOps ArgoCD Persistent Storage

The estate spans Oracle 19c and 23ai instances, each hosting multiple pluggable databases, with ORDS and APEX serving applications on top — all running as Kubernetes workloads managed by Oracle’s database operator, with storage, ingress and certificates handled by the cluster.

Knowledge separated from state

The most useful structural decision was splitting this work into two repositories.

One holds deployable state: the operator manifests, reconciled into the cluster by ArgoCD. The other holds knowledge and procedure: runbooks, lessons learned with the evidence behind each, and a troubleshooting guide indexed by the literal error strings you would paste into a search box at 3 a.m.

The reason is that state changes constantly and knowledge accumulates. Keeping them together means the reasoning behind a decision is overwritten by the next commit that changes the manifest. Keeping them apart means an incident three months later can find why, not just what.

Failure modes worth knowing

Two recur often enough to be worth naming:

Storage access modes decide your upgrade strategy. A persistent volume claim provisioned as ReadWriteOnce can only attach to one node. A rolling update tries to start the new pod before terminating the old one — so on a multi-node cluster the new pod cannot attach the volume and the rollout wedges with a multi-attach error. The fix is either ReadWriteMany storage or a recreate strategy, and the decision has to be made before the first update, not during it.

Let the operator own its resources. When an operator manages a resource, editing that resource directly produces a fight the operator wins, on its own schedule, usually after everyone has concluded the change worked. The same applies at the cloud layer: deleting a Kubernetes service that fronts a cloud load balancer lets the controller clean up the cloud resource properly, while deleting the cloud resource first orphans state the controller can no longer reconcile.

GitOps in practice

Cluster state is declared in a GitOps repository and reconciled by ArgoCD, with image tags pinned to explicit build identifiers rather than floating tags. A pinned tag means the repository records exactly which build is serving traffic, and a rollback is a revert rather than an archaeology exercise.

Sync policies are deliberate rather than uniform — some applications reconcile automatically, others require an explicit sync, depending on how much a surprise reconciliation would cost.

Ready to Build Something Similar?

Let's discuss how we can bring the same expertise to your organization.

Start a Conversation