Whitepaper: Trilio Site Recovery (TSR) — DR for Kubernetes-native VMs

Implementing persistent storage for stateful applications running in containerized environments like OpenShift is a challenging problem. There is little doubt about the flexibility of containers, but their inherent disposability can lead to all sorts of issues for stateful workloads. To persist data for such workloads, you often need to rely on specialized storage solutions that can be set up in OpenShift.

One of the most popular solutions in this regard is OpenShift Data Foundation (ODF), which is Red Hat’s integrated, software-defined storage solution for OpenShift clusters. Built on Ceph, Rook, and NooBaa, it delivers persistent block, file, and object storage directly inside the Kubernetes ecosystem.

This article is meant for engineers who already understand container storage fundamentals and want a clear, practical checklist for deploying and running ODF in real OpenShift environments. If you see daily workload, performance tuning, and production uptime challenges, you’ll want to read on.

OpenShift Data Foundation summary deployment and operations checklist

The following table provides an overview of the key ODF deployment-to-operations lifecycle stages and their purpose.

Stage

Step

Purpose

Deployment

Verify infrastructure and sizing

Confirm platform compatibility, and provision nodes with minimum CPU/RAM.

Prepare local storage

Discover raw block devices and create persistent volume mappings.

Deploy storage system

Initialize the ODF cluster with proper capacity and select the right nodes.

Validate storage services

Verify that block, file, and object storage classes are operational.

Operations

Configure day 2 operations

Implement data lifecycle management and monitoring capabilities.

Extend data Protection

Integrate CSI snapshots into developer workflows to allow for instant rollbacks and low-overhead data cloning for CI/CD testing.

Automated Application-Centric Red Hat OpenShift Data Protection & Intelligent Recovery

Why OpenShift needs a dedicated storage solution like ODF

When you move from simple proofs of concept to production-grade OpenShift clusters, the “stateless” dream usually hits a wall. Most enterprise applications like databases, logging stacks, or private registries require persistent data. While Kubernetes has made great strides with the Container Storage Interface (CSI), relying on generic, external storage often creates a massive bottleneck for Day 2 operations.

Here is why a dedicated, software-defined storage layer like OpenShift Data Foundation (ODF) is a technical necessity for OpenShift:

Eliminating “external provider” friction

In a standard setup, whenever a developer needs a persistent volume (PV), the cluster has to talk to an external SAN or a cloud-managed disk. This often leads to “impedance mismatch.” You end up managing storage through one console and your containers through another. Bringing storage inside the cluster lets you treat disk capacity just like CPU and RAM—as a native resource that the OpenShift scheduler actually understands.

The RWX problem (ReadWriteMany)

Most cloud-native storage providers excel at providing ReadWriteOnce (RWO) access, where a single pod accesses a single disk. However, the moment you need a shared file system for web uploads or log aggregation (ReadWriteMany), external providers often struggle with performance or complex mounting logic. ODF uses Ceph under the hood to provide high-performance file, block, and object storage simultaneously, so you don’t have to hunt for a different storage vendor every time a new app requirement pops up.

Solving the node-failure bottleneck

In a typical cloud setup, if a worker node goes down, you usually have to rely on the cloud provider’s API to detach and then reattach a disk to a new node. That “volume-in-use” or “stuck re-mounting” phase is detrimental to meeting your recovery time objective (RTO).

By running an integrated storage mesh, the data isn’t sitting on some distant, external array; it’s replicated across the cluster’s own nodes. If a node drops, the replacement pod doesn’t have to wait for a slow hardware handshake or network-wide data “travel” time. The bits are already present on the surviving peers in the storage cluster, turning what used to be a multi-minute recovery into a few seconds of container startup.

Consistent experience across clouds

If you’re running a hybrid deployment, for example, OpenShift on-prem (VMware / bare metal) alongside public cloud instances in AWS, the underlying storage behavior is entirely different. One uses EBS, the other uses vSphere CNS. A dedicated storage layer like ODF abstracts that hardware away. Your YAML manifests stay the same regardless of where the cluster lives because the storage API is consistent.

Automated Red Hat OpenShift Data Protection & Intelligent Recovery

Perform secure application-centric backups of containers, VMs, helm & operators

Use pre-staged snapshots to instantly test, transform, and restore during recovery

Scale with fully automated policy-driven backup-and-restore workflows

OpenShift Data Foundation architecture

Under the hood, ODF consists of a sophisticated stack of proven open-source projects, primarily Ceph, Rook, and NooBaa, orchestrated to behave as a single storage entity.

The ODF technical stack

To get why ODF is so resilient, you have to look at the three specific engines running the show:

  • Ceph: This is the foundational layer. It’s a distributed, software-defined storage cluster that treats a group of independent nodes like a single, massive pool of capacity. It handles all the background work, such as data replication, self-healing, and ensuring that even if you lose a disk (or an entire rack), your applications don’t feel the impact.
  • Rook: If Ceph is the engine, Rook is the driver. Managing a raw Ceph cluster in a containerized environment is notoriously difficult. Rook acts as the Kubernetes operator, automating the deployment, scaling, and upgrades of Ceph. It translates your high-level intents (like “I need a 50 GB volume”) into actual Ceph configurations.
  • NooBaa (Multicloud Object Gateway): This additional unit handles the object storage abstraction. It allows you to provide an S3-compatible interface to your developers regardless of where the data actually sits, whether it’s on local NVMe drives or tucked away in a public cloud bucket. It effectively decouples the application’s storage calls from the underlying infrastructure provider.

Consolidating block, file, and object storage

The real power of ODF lies in how it partitions a single set of raw physical disks into three distinct storage types. You don’t need a different vendor for each; you just use a different StorageClass:

  • Block storage (RBD): For databases like Postgres or MongoDB, every millisecond of latency is a dealbreaker. ODF handles these IOPS-heavy workloads via Ceph’s RADOS Block Device (RBD). ODF uses Ceph’s RADOS Block Device (RBD) to create and map block volumes directly to a pod using the ReadWriteOnce (RWO) mode. This gives the single pod exclusive, high-speed access to the volume, with superior performance.
  • File storage (CephFS): When you have a shared file system where multiple pods across different nodes need to read and write to the same directory simultaneously (ReadWriteMany/RWX), ODF uses CephFS. It’s a distributed file system that scales much better than traditional legacy NFS setups in a cloud-native environment.
  • Object storage (RGW and NooBaa): ODF leverages Ceph’s RADOS Gateway (RGW) and NooBaa to provide S3-compliant object storage. This is a good option for modern applications that communicate via APIs instead of mounting drives. ODF uses Ceph’s RADOS Gateway (RGW) for local high-speed object needs and NooBaa for flexible, policy-based bucket management across hybrid environments.

The CSI abstracts the entire storage backend, so your developers don’t have to understand how Ceph works. They can request a standard persistent volume claim (PVC), and the ODF operator handles all the work in the background, including provisioning the volume, handling encryption, and ensuring data replication. It turns storage into a self-service utility, so the infra team isn’t stuck manually carving out LUNs or managing mount points every time a new app goes live.

ODF deployment modes

ODF offers significant flexibility in how you roll out the storage layer. You’re essentially choosing between a hyper-converged model or a decoupled, external backend.

Internal mode

In this setup, ODF runs entirely within the OpenShift cluster as a set of pods. It uses the Local Storage Operator (LSO) to “claim” and aggregate disks, whether they are local NVMe drives, SAN volumes, or cloud-native disks such as EBS or vSphere volumes. This approach is practical when:

  • Your cluster’s storage growth isn’t yet precisely mapped, and you need to scale resources dynamically.
  • You don’t have the luxury of dedicated infrastructure nodes or “storage-only” hardware.
  • Provisioning additional node instances, especially on bare-metal servers, is a major logistical or financial hurdle.

External mode

In an external deployment, ODF connects to a standalone Red Hat Ceph Storage cluster outside the OpenShift environment. This approach treats storage as an independent service and is recommended when:

  • The storage requirements are massive enough that they would compete with applications for CPU and RAM if run internally.
  • You are managing a multi-cluster environment in which several OpenShift clusters need to consume storage from a single central source.
  • A dedicated storage team is managing the hardware, allowing you to decouple the storage lifecycle from your Kubernetes maintenance windows.

The following figure illustrates the architecture of ODF, covering both internal and external mode deployment strategies, and shows how the CSI drivers bridge the gap between your application pods and the underlying storage.

Architecture of OpenShift Data Foundation

Architecture of OpenShift Data Foundation

Validating infrastructure and sizing: Preparing a cluster for OpenShift Data Foundation

Deploying ODF isn’t a “one-size-fits-all” task. You can’t just follow a generic guide, because a bare-metal setup with local NVMe drives requires an entirely different mindset and a different provisioner than a managed cloud environment like ROSA or GCP. Therefore, it is recommended to audit your platform’s specific storage constraints and supported modes to avoid hitting a configuration dead end mid-install. The underlying infrastructure should dictate the architecture for you. 

Platform requirements

The following table summarizes the supported deployment modes and recommended storage provisioners across the major infrastructure providers:

Platform

Supported Modes

Storage Provisioner / Requirements

Amazon EC2

Internal only

Supports gp2-csi and gp3-csi; gp3 is recommended for high throughput. D2/D3 instances are best for high IOPS.

Bare Metal

Internal and external

Requires Local Storage Operator (LSO). Production nodes need at least 0.5 TiB per storage device.

VMware vSphere

Internal and external

Recommended for vSphere 8.0+. Uses vsphere-volume for vSAN/VMFS or LSO for direct VMDK/DirectPath access.

Microsoft Azure

Internal only

Requires a storage class providing Azure Disks via the azure-disk provisioner.

Google Cloud

Internal only

Requires GCE Persistent Disks via the gce-pd provisioner.

IBM Power / Z

Internal and external

Requires LSO for local SSD/SAN. IBM Z supports external mode specifically when Ceph runs on x86.

ROSA (HCP)

Internal only

Strictly uses AWS EBS volumes via the gp3-csi provisioner.

Validating the backend for external mode deployment

For external mode deployments, you need to validate the integration of RedHat Ceph Storage cluster with ODF. If your Red Hat Ceph Storage (RHCS) version isn’t perfectly aligned with your ODF release, the CSI drivers will likely run into API mismatches or silent communication failures. Before you commit to an external architecture, verify that your specific Ceph version is validated for the ODF 4.20 lifecycle.

To ensure that your external Red Hat Ceph Storage (RHCS) cluster is officially supported for your ODF release, use the Red Hat Supportability and Interoperability Checker:

  1. Service Type: Set this to ODF as Self-Managed Service.
  2. Version Selection: Select the appropriate ODF version from the dropdown.
  3. Compatibility Mapping: Under the Versions tab, click Supported RHCS Compatibility to see the validated Ceph releases for that build.

The following figure illustrates the utility of the Red Hat Interoperability Checker in verifying infrastructure compatibility.

 

Validating ODF design via the Red Hat Interoperability Checker

Validating ODF design via the Red Hat Interoperability Checker

Resource requirements overview

ODF services carry their own overhead, so you need to account for the overhead that ODF requires on top of your existing OpenShift footprint. These numbers represent what the ODF services themselves consume; they don’t include the resources needed for your actual application workloads.

A standard production rollout typically follows the “rule of three,” where services are spread across three nodes (one per failure domain) to satisfy pod-placement rules. The following table breaks down the aggregate requirements for the ODF base services.

Deployment Mode

Total CPU (Logical)

Total Memory (RAM)

Storage Devices

Internal (Standard)

30 units

72 GiB

3 devices

Internal (Minimum)

24 units

72 GiB

3 devices

External

4 units

16 GiB

N/A

Compact (3-node)

24 units

72 GiB

3 devices

In addition, if you plan to enable the NFS service for external access, you’ll need to reserve an additional 3 CPUs and 8 GiB of RAM.

When calculating these requirements, keep in mind that 1 CPU unit in the Red Hat documentation maps directly to a Kubernetes CPU unit:

  • For non-hyperthreaded CPUs, 1 unit = 1 physical core.
  • For hyperthreaded CPUs, 2 units = 1 physical core.

It’s always safer to overprovision slightly. If your worker nodes are already running at high utilization, ODF might struggle to maintain data replication speeds during peak IO periods.

Storage device guidelines

To keep your cluster performant and reliable, keep these in mind:

  • The “rule of three”: Always expand your cluster in multiples of three. By placing one node in each failure domain (or rack), you ensure that ODF can satisfy its internal pod-placement and data-replication rules.
  • Dedicated disks: Each node requires at least two disks, one dedicated solely to the operating system and the others reserved for ODF.
  • Device limits: While you can scale up, it is generally recommended to keep the number of devices per node to 12 or fewer. This will let you avoid hitting the cloud provider’s limits on attached volumes and keep data recovery times manageable if a node ever goes down.
  • Configure failure domains: To ensure data resilience, configure ODF to recognize failure domains (such as different racks or zones). By implementing anti-affinity rules, the system ensures that three-way replication spreads data across these domains, allowing the cluster to survive a hardware failure without data loss.

Capacity and scaling

The way you provision storage depends on whether you are using dynamic cloud volumes or local physical disks:

  • Dynamic storage (cloud): You can typically request sizes of 0.5 TiB, 1 TiB, 2 TiB, 4 TiB, or 8 TiB. Note: You can only expand capacity in increments of the size you selected at installation. If you start with 2 TiB disks, your expansion sets must also be 2 TiB.
  • Local storage (bare metal / VMware): You can use disks up to 16 TiB. For the sake of cluster balance and predictable performance, ensure that all disks in a set are the same size and type (e.g., all 1.9 TB NVMe).

Deploying OpenShift Data Foundation

We’re now going to see how to set up ODF services in internal mode in our OpenShift cluster. For this demo, the OpenShift cluster is running on VMware.

Please ensure that the following are true:

  • Each of the selected nodes must have at least one raw block device available for use by ODF.
  • The block devices must be empty and must not contain any existing configurations.
  • For VMware, ensure that the disk.EnableUUID option is set to TRUE for each VM.

Operator installation

Log into your OpenShift Web Console with cluster-admin privileges and follow these steps to get the necessary operators running:

  1. OpenShift Data Foundation Operator:
    • Navigate to Operators > OperatorHub.
    • Filter for OpenShift Data Foundation.
    • Select the Operator, click Install, and accept the default settings (stable channel, cluster-wide).
  2. Local Storage Operator:
    • Return to OperatorHub and filter for Local Storage.
    • Select the operator and click Install, again using the default settings.

Preparing local storage for ODF

Next, we need to tell the LSO how to “claim” the disks on your nodes so they can be handed over to ODF. This involves two main tasks: discovery and provisioning.

Discovery

  1. Disk discovery

You need to identify which disks are actually available across your worker nodes without manually logging into every machine:

  • Go to Installed Operators > Local Storage > Local Volume Discovery.
  • Create a Local Volume Discovery instance and select the specific nodes you’ve designated for ODF.
  • The Operator will scan these nodes. You can verify the results via the CLI to see which drives are ready for use:
  1. Identifying reliable device IDs

When you look at the discovery results, you’ll see disks listed with standard paths like /dev/sdb.

A word of caution: Avoid using /dev/sdX paths. These are not stable; if a node reboots, the OS might discover drives in a different order, potentially breaking your storage configuration.

Best practice: Always use the unique Device ID (found in the discovery details). These IDs are persistent and tied to the hardware itself, ensuring that your cluster remains stable across reboots and maintenance windows.

$ oc get localvolumediscoveryresults
NAME                              	AGE
discovery-result-ocpnode-1            1m
discovery-result-ocpnode-2            1m
discovery-result-ocpnode-3            1m
$ oc describe localvolumediscoveryresults  discovery-result-ocpnode-1 |less
[...............]
	Device ID:  /dev/disk/by-id/wwn-0x6000c29c01d91ed7b7109f82c40d42e7
	Fstype:	 
	Model:  	Virtual disk
	Path:   	/dev/sdc
	Property:   Rotational
	Serial: 	6000c29c01d91ed7b7109f82c40d42e7
	Size:   	107374182400
	Status:
  	  State:	Available
[.................]

Once you’ve mapped out your physical drives and secured those persistent Device IDs, the final phase is bridging that hardware into the OpenShift ecosystem. This is where the Local Volume Set meets the ODF Storage System to create the actual usable storage classes for your applications.

Provisioning the local volumes

With your Device IDs in hand, you need to bind them into a storage class that ODF can consume.

  1. Create Local Volumes: In the console, navigate to Installed Operators > Local Storage > Local Volume and click Create Local Volume.
  2. Map the Paths: Assign a name to your volume set. Under StorageClassDevices > devicePaths, paste the unique Device IDs you identified during the discovery phase. If you have multiple drives, use Add devicePaths to include them all.
  3. Define the Class: Set your storageClassName and ensure the volumeMode is set to Block. Leave the remaining defaults and hit create.
  4. Verification: Once the process finishes, verify that a new StorageClass exists and that individual Persistent Volumes (PVs) have been automatically generated for each physical disk.

The following figure demonstrates the configuration and provisioning of local storage devices for use within the cluster.

Configuration of local storage devices via the Local Storage Operator

Configuration of local storage devices via the Local Storage Operator

Configuration of local storage devices via the Local Storage Operator

$ oc get sc
NAME   PROVISIONER                  RECLAIMPOLICY   VOLUMEBINDINGMODE  	ALLOWVOLUMEEXPANSION   AGE
lso   kubernetes.io/no-provisioner      Delete      	WaitForFirstConsumer
  false                 1h
$ oc get pv
NAME                                   	CAPACITY   ACCESS MODES   RECLAIM POLICY   STATUS   CLAIM                                           	STORAGECLASS              	REASON   AGE
local-pv-3cfceaf                       	200Gi   	RWO        	Delete       	Bound	openshift-storage/ocs-deviceset-lso-0-data-4fgmfr   lso                                	1h
local-pv-9ccedf9f                      	200Gi   	RWO        	Delete       	Bound	openshift-storage/ocs-deviceset-lso-0-data-5zz5lk   lso                                	1h
local-pv-a862d6ec                      	200Gi   	RWO        	Delete       	Bound	openshift-storage/ocs-deviceset-lso-0-data-15bsz7   lso                                	1h
local-pv-b98bce49                      	1200Gi   	RWO        	Delete
[.........................]

Deploying the ODF storage system

Now that the underlying local volumes are ready, you can initialize the core ODF cluster:

  1. Initialize: Go to Installed Operators > OpenShift Data Foundation > Storage System and click Create Storage System.
  2. Select Strategy: Choose Full deployment and Use an existing StorageClass. Select the StorageClass you just created in the previous step.
  3. Capacity & Nodes: Enter your Requested Capacity and select the specific worker nodes where your disks are physically attached.
  4. Finalize: You can typically stick with the default Security, Network, and Data Protection settings for a standard rollout. Proceed to create the system.

The following figures outline the creation of the storage system and the initial cluster configuration within the platform.

Storage system creation

Storage system creation

Defining backing storage for ODF cluster

Defining backing storage for ODF cluster

Defining backing storage deployment options

Defining backing storage deployment options

Defining ODF cluster capacity

Defining ODF cluster capacity

Defining storage system parameters

Defining storage system parameters

It takes a few minutes for the openshift-storage project to pull images and spin up the Ceph mon, osd, and mgr pods. You can monitor the progress by watching the pods in that namespace. Once the storage cluster reports a Ready status, your environment is live.

$ watch oc get storagecluster,pods -n openshift-storage

Once the storage cluster has been created successfully, the command will report Ready status.

$ oc get storagecluster -n openshift-storage
NAME             	AGE   PHASE   EXTERNAL   CREATED AT         	VERSION
ocs-storagecluster   10m   Ready          	2026-02-11T11:3:06Z   4.18.16

This can also be verified from the console.

Successful ODF cluster creation

Successful ODF cluster creation

Validating ODF storage services: Block, file, and object use cases

Upon successful deployment, ODF provides four distinct storage classes, each designed for specific architectural needs. 

$ oc get sc
NAME                                     	PROVISIONER                         	RECLAIMPOLICY   VOLUMEBINDINGMODE  	ALLOWVOLUMEEXPANSION   AGE
lso                                      	kubernetes.io/no-provisioner        	Delete      	WaitForFirstConsumer   false              	1h
ocs-storagecluster-ceph-rbd    	openshift-storage.rbd.csi.ceph.com  	Delete      	Immediate          	true               	1h
ocs-storagecluster-ceph-rgw              	openshift-storage.ceph.rook.io/bucket   Delete      	Immediate          	false              	1h
ocs-storagecluster-cephfs                	openshift-storage.cephfs.csi.ceph.com   Delete      	Immediate          	true               	1h
openshift-storage.noobaa.io              	openshift-storage.noobaa.io/obc     	Delete      	Immediate          	false              	1h

The recommended use cases of these storage classes are listed below.

Storage ClassBacking TechnologyPrimary Use Case
ocs-storagecluster-ceph-rbdCeph Block DeviceHigh-performance RWO workloads (databases, transactional apps)
ocs-storagecluster-cephfsCeph File SystemShared, distributed RWX storage (logging, data aggregation, file share services, backup archives)
openshift-storage.noobaa.ioMulticloud Object GatewayS3-compatible API endpoint for abstracting multiple cloud stores
ocs-storagecluster-ceph-rgwCeph RADOS GatewayOn-premises object storage for data-intensive S3 applications

Once the cluster is healthy, you can start putting that storage to work. The ocs-storagecluster-ceph-rbd storage class is typically your workhorse for high-performance, block-backed volumes.

Examining the RBD storage class

To understand exactly how ODF is handling your data requests, you can inspect the storage class definition. This gives you a look at the CSI parameters and the underlying Ceph pool configuration.

$$ oc describe sc ocs-storagecluster-ceph-rbd
Name:              	ocs-storagecluster-ceph-rbd
IsDefaultClass:    	No
Annotations:       	description=Provides RWO Filesystem volumes, and RWO and RWX Block volumes,storageclass.kubernetes.io/is-default-class=true
Provisioner:       	openshift-storage.rbd.csi.ceph.com
Parameters:        	clusterID=openshift-storage,csi.storage.k8s.io/controller-expand-secret-name=rook-csi-rbd-provisioner,csi.storage.k8s.io/controller-expand-secret-namespace=openshift-storage,csi.storage.k8s.io/fstype=ext4,csi.storage.k8s.io/node-stage-secret-name=rook-csi-rbd-node,csi.storage.k8s.io/node-stage-secret-namespace=openshift-storage,csi.storage.k8s.io/provisioner-secret-name=rook-csi-rbd-provisioner,csi.storage.k8s.io/provisioner-secret-namespace=openshift-storage,imageFeatures=layering,deep-flatten,exclusive-lock,object-map,fast-diff,imageFormat=2,pool=ocs-storagecluster-cephblockpool
AllowVolumeExpansion:  True
MountOptions:      	
ReclaimPolicy:     	Delete
VolumeBindingMode: 	Immediate
Events:  none

The output reveals several critical operational details:

  • IsDefaultClass: No: While it’s a primary class, it won’t be used for every generic PVC unless you explicitly mark it as the default for the cluster.
  • Provisioner: This uses openshift-storage.rbd.csi.ceph.com, signaling that the Ceph RBD CSI driver is managing the lifecycle of these volumes.
  • Parameters: Key values like pool=ocs-storagecluster-cephblockpool and fstype=ext4 tell you exactly where the data lives in Ceph and how the filesystem is formatted upon creation.
  • AllowVolumeExpansion: True: This is vital for Day 2 operations; you can grow your volumes on the fly without unmounting them from your pods.
  • Reclaim Policy: Delete: By default, ODF will clean up the backend Ceph image as soon as the PVC is deleted in OpenShift to prevent “orphaned” storage.

Let’s validate the volume provisioning and create a test volume using a standard manifest:

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: ocs-storagecluster-ceph-rbd
  volumeMode: Filesystem
  resources:
    requests:
      storage: 1Gi

Apply the manifest and verify the volume creation.

$ oc apply -f test-pvc.yaml
persistentvolumeclaim/test-pvc created
$ oc get pvc -n default
NAME    	STATUS   VOLUME                                 	CAPACITY   ACCESS MODES   STORAGECLASS              	AGE
block-pvc   Bound	pvc-34c76ey6-7845-4e46-8f01-04f8a69f3d43   1Gi    	RWO        	ocs-storagecluster-ceph-rbd   12s
$ oc get pv |grep test-pvc
pvc-34c76ey6-7845-4e46-8f01-04f8a69f3d43   1Gi    	RWO        	Delete       	Bound	default/block-pvc                               	ocs-storagecluster-ceph-rbd        	35s

At this point, the backend storage is fully carved out and ready. Your application pods can simply reference the test-pvc in their volume definitions and start consuming storage.

Examining the CephFS storage class

For workloads that need to share a common directory across multiple nodes, like distributed logging or a central content repo, you need ReadWriteMany (RWX) support. This is the primary use case for ocs-storagecluster-cephfs. While RBD is perfect for high-speed block storage, it’s limited to a single node at a time; CephFS, however, is a distributed file system that manages concurrent read/write operations across your entire cluster.

If you pull the metadata for this storage class, you’ll see a different backend driver:

$ oc describe storageclass ocs-storagecluster-cephfs
Name: ocs-storagecluster-cephfs
IsDefaultClass: No
Annotations: description=Provides RWO and RWX Filesystem volumes
Provisioner: openshift-storage.cephfs.csi.ceph.com
Parameters: clusterID=openshift-storage,csi.storage.k8s.io/controller-
expand-secret-name=rook-csi-cephfs-provisioner,csi.storage.k8s.io/controller-
expand-secret-namespace=openshift-storage,csi.storage.k8s.io/node-stage-
secret-name=rook-csi-cephfs-node,csi.storage.k8s.io/node-stage-secret-
namespace=openshift-storage,csi.storage.k8s.io/provisioner-secret-name=rook-csi-
cephfs-provisioner,csi.storage.k8s.io/provisioner-secret-namespace=openshift-
storage,fsName=ocs-storagecluster-cephfilesystem
AllowVolumeExpansion: True
MountOptions: 
ReclaimPolicy: Delete
VolumeBindingMode: Immediate
Events: none

The output reveals the following:

  • Cephfs uses openshift-storage.cephfs.csi.ceph.com. Instead of mapping a raw block device, this provisioner creates subvolumes within the Ceph File System (fsName=ocs-storagecluster-cephfilesystem).
  • The most important feature here is RWX support. This allows you to scale your deployment across multiple replicas while ensuring that every pod sees the same directory structure and files.
  • Like RBD, it supports immediate volume binding and online expansion. You don’t have to wait for a pod to be scheduled before provisioning the volume, and you can grow the shared space as your data grows.

To consume storage from CephFS, we’ll need to create a PVC that explicitly requests RWX mode from the CephFS storage class.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: test-pvc
spec:
  accessModes:
  - ReadWriteMany
  storageClassName: ocs-storagecluster-cephfs
  resources:
    requests:
      storage: 1Gi

Examining the object storage classes

When you’re dealing with large scale archives, media or unstructured data that doesn’t need a traditional filesystem hierarchy, you move into object storage. Instead of mounting a drive, your apps talk to an S3 API endpoint. This is the “flat” storage model that scales almost infinitely.

For object storage, ODF gives you the flexibility to choose your provider based on where you want that data actually to live.

Ceph RADOS Gateway (RGW)

If you need high-performance object storage that stays strictly on your local NVMe or SSD tiers, Ceph RGW is the play. It’s a RESTful gateway that sits directly on top of your Ceph cluster, providing a native S3 interface without any external dependencies.

  • Storage Class: ocs-storagecluster-ceph-rgw
  • When to use it: Local database backups, high-speed logging, or any application where data sovereignty and low latency are the top priorities

Multicloud Object Gateway (NooBaa)

For environments that bridge local data with the public cloud, NooBaa (MCG) is the standard. It acts as an abstraction layer, a gateway that can consume storage from AWS, Azure, or other S3-compatible backends and present them as a single, unified bucket to your developers.

  • Storage Class: openshift-storage.noobaa.io
  • When to use it: Hybrid-cloud tiering, disaster recovery syncs to the cloud, or when you want to decouple your application’s S3 endpoint from the underlying physical hardware

Provisioning with object bucket claims (OBC)

For consuming object storage, the equivalent to a PVC is an object bucket claim (OBC). When you create an OBC, the ODF operator (NooBaa or RGW) creates a ConfigMap and a secret in the OBC namespace. These two resources act as the bridge between

apiVersion: objectbucket.io/v1alpha1
kind: ObjectBucketClaim
metadata:
  name: archive-storage-obc
  namespace: my-obc-project
spec:
  generateBucketName: app-archive
  storageClassName: openshift-storage.noobaa.io
$ oc create -f obc.yaml
objectbucketclaim.objectbucket.io/archive-storage-obc created
$ oc get objectbucketclaims
NAME                      STORAGE-CLASS         PHASE AGE
archive-storage-obc openshift-storage.noobaa.io Bound  6s
oc get objectbuckets
NAME                                STORAGE-CLASS ...           PHASE
obc-default-archive-storage-obc openshift-storage.noobaa.io ... Bound

As soon as the OBC resource is created, the following resources are created to handle the lifecycle and security of the storage:

  • Object bucket (OB): This is the cluster-scoped representation of the actual S3 bucket. It tracks the backend metadata and ensures that the physical storage exists on either NooBaa or Ceph RGW.
  • ConfigMap (CM): The CM contains the S3 endpoint (host and port) and the specific bucket name. Since this is non-sensitive routing data, it’s stored in plain text for easy injection.
  • Secret: The secret contains the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY. ODF generates these unique credentials specifically for the object claim, ensuring that your application follows the principle of least privilege.
$ oc extract configmap/archive-storage-obc --to=-
# BUCKET_HOST
s3.openshift-storage.svc
# BUCKET_PORT
443
# BUCKET_NAME
archive-storage-obc-14f6ce1f-11ac-67ec-b8a5-1a74427cfb1
[.....]
oc extract secret/archive-storage-obc --to=-
# AWS_ACCESS_KEY_ID
YZDsbNPnG1o23AGDZprt
# AWS_SECRET_ACCESS_KEY
xjreDRhswx3lfrdA8WqzoUxpiRYuyjc2uPlWpMw3

The environment variables from the configuration map and secret can then be referenced inside the application manifest to consume object storage.

Configuring ODF Day 2 operations

Once your storage is provisioned and the applications are running, your focus shifts to data lifecycle management. In ODF, snapshots and clones are the two primary tools for ensuring data recoverability and streamlining your dev/test workflows without overhead.

Data recovery with volume snapshots

A volume snapshot is a point-in-time copy of your data. Think of it as a “safety net” before performing risky operations, such as database migrations or application upgrades. If something goes sideways, you can restore the persistent volume to its exact state at the moment the snapshot was taken.

The Ceph RBD and CephFS storage classes have CSI drivers that provide the snapshot functionality for PVCs.

$ oc get volumesnapshotclasses
NAME                                      DRIVER
ocs-storagecluster-rbdplugin-snapclass    openshift-storage.rbd.csi.ceph.com
ocs-storagecluster-cephfsplugin-snapclass openshift-storage.cephfs.csi.ceph.com

You can create a snapshot of a PVC by referencing the source PVC in the persistentVolumeClaimName parameter and specifying the volume snapshot class for Ceph RBD or CephFS.

apiVersion: snapshot.storage.k8s.io/v1
kind: VolumeSnapshot
metadata:
  name: pvc-data-snapshot
spec:
  volumeSnapshotClassName: ocs-storagecluster-rbdplugin-snapclass
  source:
    persistentVolumeClaimName: pvc-data

After the volume snapshot is created, a new PVC can be created from it. The volume snapshot must be referenced in the dataSource parameter of the new PVC.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: pvc-data-restore
  labels:
    app: data
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: ocs-storagecluster-ceph-rbd
  dataSource:
    apiGroup: snapshot.storage.k8s.io
    kind: VolumeSnapshot
    name: pvc-data-snapshot
  resources:
    requests:
      storage: 1Gi

Testing and debugging with volume clones

While snapshots are for recovery, clones are for utility. A volume clone is a duplicate of an existing persistent volume and is invaluable when you need to debug a production issue using real data without actually touching the production environment.

You can create a clone of a PVC by referencing the source PVC in the dataSource parameter. You need to reference the source PVC from which to take the clone. Once the clone has been created, it can be used just like any other PVC.

apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: data-pvc-clone
  labels:
    app: data
spec:
  accessModes:
  - ReadWriteOnce
  storageClassName: ocs-storagecluster-ceph-rbd
  dataSource:
    kind: PersistentVolumeClaim
    name: data-pvc
  resources:
    requests:
      storage: 1Gi

Dynamic volume expansion

One of the most common Day 2 requests is the need for more space as a database or log repository grows. In ODF, you can expand a PVC on the fly without having to delete the volume or restart your application.

This capability is controlled by the allowVolumeExpansion attribute in the StorageClass. In ODF, both the RBD and CephFS storage classes have this set to true by default.

To trigger an expansion, you modify the spec.resources.requests.storage value in your existing PVC manifest.

$ oc edit pvc/data
[......]
spec:
  accessModes:
  - ReadWriteMany
  resources:
    requests:
      storage: 200Gi
  storageClassName: ocs-storagecluster-cephfs
  volumeMode: Filesystem
  volumeName: pvc-652f16c-2d09-4dc1-ae09-27034a5ded8c
[......]

Monitoring the ODF cluster

To truly maintain a healthy ODF environment, you have to move beyond just provisioning and into active performance monitoring. Red Hat OpenShift provides a deep, integrated observability stack that surfaces these storage-specific metrics directly within the administrator console.

Monitoring ODF cluster health

Monitoring ODF cluster health

You can also see performance data for metrics such as IOPs, throughput and latency.

Monitoring the performance of the ODF cluster

Monitoring the performance of the ODF cluster

To check the health of your storage pools, navigate to Storage > Data Foundation in the Administrator perspective of the OpenShift web console. Under the Storage Systems tab, click on your specific storage system to view the Status card, which provides a real-time health indicator and any active alerts for the underlying Ceph pools.

Monitoring the health of storage pools

Monitoring the health of storage pools

Monitoring the health of storage pools

Extending ODF data protection with Trilio

For complex enterprise environments, the resilient storage layer provided by ODF is improved by complementing it with Trilio, an application-centric data protection solution. ODF handles the physical availability of data, while Trilio provides the management framework needed for full-spectrum disaster recovery and application mobility.

Application-centric data protection

Trilio extends ODF’s basic CSI snapshot capabilities by capturing more than just raw data volumes. It groups persistent volumes, Kubernetes metadata (such as ConfigMaps, Secrets, and Services), and associated objects into a single, restorable Application Vault. This ensures that when you restore an application, it comes back as a fully functional unit, not just a collection of disconnected disks.

High-speed local backups

One unique advantage of Trilio’s integration is the ability to use ODF’s native Multicloud Object Gateway (NooBaa) as the primary storage target for Trilio’s backups. By targeting a local S3-compatible bucket, backup and recovery traffic remains internal to the cluster. This significantly reduces recovery time objectives (RTO) by avoiding the latency and egress costs associated with external cloud storage.

Seamless multi-cluster mobility

Beyond simple backups, Trilio enables true application mobility across the hybrid cloud. By abstracting the underlying ODF storage layer, Trilio allows you to migrate entire workloads between different OpenShift clusters, for instance, moving a stateful application from an on-premises datacenter to AWS. Because Trilio packages the data and the application’s “brain” (the YAML manifests) together, the application can be re-provisioned on any ODF-backed cluster with minimal configuration changes.

Recommended practices for running ODF

The following are some of the recommended practices for running ODF:

  • Finalize topology before deployment: Evaluate the workload requirements early to decide between internal and external mode deployments.
  • Allocate dedicated resources: Validate that every node meets the specific CPU and RAM requirements for OSDs to prevent out-of-memory events and maintain consistent I/O throughput.
  • Configure failure domains: Implement affinity and anti-affinity rules to spread data across different racks or availability zones, ensuring that three-way replication can survive a hardware failure.
  • High speed storage: Use high-speed storage devices as a backend for improved performance.
  • Automate via OBCs: Standardize on Object Bucket Claims (OBC) for all S3 traffic to automate credential rotation and ensure application portability.
  • Validate snapshots: Periodically test your recovery path by spinning up a new PVC from an existing snapshot to confirm data integrity.

Learn How To Best Backup & Restore Virtual Machines Running on OpenShift

Conclusion

Building a production-grade Kubernetes strategy means moving away from fragmented, hardware-dependent storage. Red Hat OpenShift Data Foundation (ODF) addresses this need by providing a software-defined abstraction that treats block, file, and object storage as native, first-class citizens. Regardless of the storage requirements of the workload, ODF provides a single, unified interface that scales with the cluster.

The real value here is decoupling application logic from the underlying infrastructure. Automating the provisioning and lifecycle of these storage classes means that cluster administrators no longer have to manually manage individual disk mappings. This creates a consistent storage experience across on-premises datacenters and the public cloud, ensuring that data remains as portable and agile as the containerized applications themselves.

Table Of Contents

Like This Article?

Subscribe to our LinkedIn Newsletter to receive more educational content

Privacy Overview

This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.