Trilio Site Recovery for Kubernetes/OpenShift Virtualization
Guide

Custom Resource Reference

Complete API reference for all site recovery custom resources


Overview

This page is the complete API reference for all Custom Resource Definitions (CRDs) shipped with Trilio Site Recovery for OpenShift Virtualization. Each CRD entry describes its purpose, the cluster where it lives, its spec fields, its status fields, and the lifecycle phases or conditions you will encounter when operating DR workflows. Understanding these resources is essential because all DR orchestration—protecting VMs, monitoring replication health, triggering failovers, and auditing RPO compliance—is expressed entirely through these Kubernetes-native objects that you create, inspect, and delete with kubectl.


Prerequisites

Before working with these custom resources, ensure the following are in place:

  • Kubernetes ≥ 1.28 or OpenShift ≥ 4.14 on all three clusters (quorum, primary, DR)
  • KubeVirt ≥ 1.0 installed on the primary and DR clusters
  • Helm ≥ 3.0 (used to deploy operators that install these CRDs)
  • DRBD kernel module ≥ 9.0 on every worker node in the primary and DR clusters
  • site-recovery-protectionzone-controller deployed on the quorum cluster before the quorum control plane chart — this operator installs the CRDs themselves
  • site-recovery-quorum-control-plane deployed on the quorum cluster — hosts the reconcilers that watch and act on these CRDs
  • site-recovery-workload-control-plane deployed on both the primary and DR clusters
  • Kubeconfig credentials for the primary and DR clusters stored as Secrets on the quorum cluster
  • TCP port 6443 open from the quorum cluster to both workload cluster API servers
  • TCP ports 7000–7999 open between primary and DR worker nodes for DRBD replication
  • For DRBD Operator deployments: the DRBD Operator installed on both the primary and DR clusters

Note: Test failover (TestFailover) is supported only in the DRBD Operator deployment model.


Installation

CRDs are installed automatically when you deploy the site-recovery-protectionzone-controller Helm chart on the quorum cluster. You do not apply CRD manifests manually.

Step 1 — Add the Trilio Helm repository

helm repo add trilio https://charts.trilio.io
helm repo update

Step 2 — Deploy the ProtectionZone controller (installs CRDs)

This chart must be deployed first. It registers all site recovery CRDs with the quorum cluster's API server.

helm install site-recovery-protectionzone-controller trilio/site-recovery-protectionzone-controller \
  --namespace trilio-system \
  --create-namespace

Verify the CRDs are registered:

kubectl get crds | grep siterecovery.trilio.io

Expected output includes entries such as:

protectiongroups.siterecovery.trilio.io
protectionrequests.siterecovery.trilio.io
failoverrequests.siterecovery.trilio.io
testfailovers.siterecovery.trilio.io
drbdreplicationpolicies.siterecovery.trilio.io
drbdresources.siterecovery.trilio.io
rpoevents.siterecovery.trilio.io
replicationgroupstatuses.siterecovery.trilio.io

Step 3 — Deploy the quorum control plane

The quorum control plane hosts the reconcilers that act on these CRDs. Each DR deployment runs in its own namespace named dr-<deployment-name>.

helm install site-recovery-quorum-control-plane trilio/site-recovery-quorum-control-plane \
  --namespace dr-prod \
  --create-namespace \
  --set deploymentName=prod

Step 4 — Deploy the workload control plane on each workload cluster

Run this on both the primary and DR clusters:

helm install site-recovery-workload-control-plane trilio/site-recovery-workload-control-plane \
  --namespace trilio-system \
  --create-namespace

The workload control plane deploys a Deployment and a DaemonSet (drbd-node-agent) on every worker node.


Configuration

Each CRD has its own spec fields. The table below summarizes the key configurable fields per resource and their effects.


ProtectionGroup

FieldTypeDefaultDescription
spec.virtualMachines[]VMRefrequiredList of {name, namespace} pairs identifying the VMs to protect as a group.
spec.sourceClusterClusterID""Designates which workload cluster (cluster1 or cluster2) holds the authoritative Primary DRBD copy. Used as a tie-breaker when neither copy reports Primary role.
spec.sla.rtoTargetSecondsint32required if sla is setTarget recovery time in seconds. Minimum value: 1.
spec.sla.rpoTargetSecondsint320Target recovery point in seconds. 0 means RPO=0 (synchronous). The replication monitor propagates this value to the associated ReplicationGroupStatus.

ProtectionRequest (DRBD Operator model)

FieldTypeDefaultDescription
spec.vmNamestringrequiredName of the VirtualMachine resource to protect.
spec.vmNamespacestringrequiredNamespace containing the VM.
spec.replicationPolicyRefstringrequiredName of the DRBDReplicationPolicy to use for this VM. The policy must already exist.

FailoverRequest

FieldTypeDefaultDescription
spec.protectionGroupRefstringrequiredName of the ProtectionGroup to fail over.
spec.typestringrequiredPlanned (graceful, zero data loss) or Unplanned (disaster, immediate promotion).
spec.targetClusterClusterIDrequiredThe cluster (cluster1 or cluster2) to which VMs will be started after failover.

TestFailover (DRBD Operator model only)

FieldTypeDefaultDescription
spec.protectionGroupRefstringrequiredName of the ProtectionGroup to test.
spec.targetNamespacestringrequiredIsolated namespace on the DR cluster where snapshot-based VM copies are created.

DRBDReplicationPolicy (DRBD Operator model)

FieldTypeDefaultDescription
spec.protocolstringrequiredReplication protocol: A (asynchronous, RPO=seconds) or C (synchronous, RPO=0). Protocol C requires network round-trip latency below ~50ms between clusters.
spec.storageClassMappingsmap[string]stringrequiredMaps primary cluster storage class names to DR cluster storage class names.
spec.primaryEndpoints[]stringrequiredDRBD endpoint addresses (IP:port) on the primary cluster worker nodes.
spec.drEndpoints[]stringrequiredDRBD endpoint addresses (IP:port) on the DR cluster worker nodes.

ReplicationGroupStatus

FieldTypeDefaultDescription
spec.protectionGroupRefPGRefrequired{name, namespace} of the owning ProtectionGroup.
spec.replicationProtocolstringCReplication protocol (A or C). Propagated from the DRBDReplicationPolicy.
spec.rpoObjectiveSecondsint320RPO objective in seconds, propagated from the PG's SLA. The replication monitor updates this when the PG's SLA changes; if the PG's SLA is removed, a manually tuned value is preserved.
spec.pollingIntervalSecondsint3215How often (in seconds) the replication monitor scrapes DRBD metrics and patches this resource's status. Overrides the deployment-wide DEFAULT_SCRAPE_INTERVAL.

Usage

All site recovery operations are performed by creating or deleting custom resource manifests and then observing status fields with kubectl. The workflows below cover the most common operator tasks.


Protecting a VM (DRBD Operator model)

Before protecting any VM, a DRBDReplicationPolicy must exist in the deployment namespace.

# Apply the replication policy first
kubectl apply -f drbd-replication-policy.yaml --namespace dr-prod

# Then create a ProtectionRequest for each VM
kubectl apply -f protection-request-vm1.yaml --namespace dr-prod

Watch the protection lifecycle progress through its phases:

kubectl get protectionrequest vm1-protect --namespace dr-prod -w

Phases progress from Pending → validation → DRBD volume creation → data synchronization → Protected. When the ProtectionRequest reaches the Protected phase, the VM's disk is backed by a DRBD-replicated (Frontend) PVC and replication to the DR cluster is active.


Checking replication health

The replication monitor automatically creates a ReplicationGroupStatus for every ProtectionGroup. Query it to see aggregate health:

kubectl get replicationgroupstatus pg-web-tier-replication --namespace dr-prod -o yaml

Key status fields to inspect:

  • status.rpoStatusMet, Degraded, Violated, or Unknown
  • status.aggregated.connectionStateConnected or Degraded
  • status.aggregated.totalVolumeCount / syncedVolumeCount
  • status.volumeReplicationStatuses[] — per-VM breakdown with outOfSyncBytes, connectionState, replicationState, and estimatedRPOSeconds
  • status.lastFullySyncedAt — timestamp of the last full synchronization

Auditing RPO violations

The replication monitor creates an immutable RPOEvent whenever the rpoStatus transitions (for example, from Met to Violated). List all events for a protection group:

kubectl get rpoevents --namespace dr-prod \
  -l siterecovery.trilio.io/protection-group=pg-web-tier

Each RPOEvent records the previous and new status, the violation reason, the out-of-sync byte count at the time of the event, and a timestamp.


Triggering a planned failover

Create a FailoverRequest with spec.type: Planned:

kubectl apply -f failover-request-planned.yaml --namespace dr-prod

The failover controller orchestrates VM shutdown on the primary cluster, DRBD volume promotion on the DR cluster, and VM startup on the DR cluster. Monitor progress:

kubectl get failoverrequest pg-web-tier-failover --namespace dr-prod -w

Running a non-disruptive DR test (DRBD Operator model only)

Create a TestFailover resource. The controller creates snapshot-based copies of protected VMs in an isolated namespace on the DR cluster, runs verification checks, and cleans up — without affecting production VMs:

kubectl apply -f test-failover.yaml --namespace dr-prod

Monitor the test:

kubectl get testfailover pg-web-tier-test --namespace dr-prod -w

When the test completes, all resources in the isolated namespace are cleaned up automatically.


Examples

Example 1 — DRBDReplicationPolicy with synchronous replication (Protocol C)

Use this for clusters with sub-50ms round-trip latency to achieve RPO=0.

apiVersion: siterecovery.trilio.io/v1alpha1
kind: DRBDReplicationPolicy
metadata:
  name: policy-sync
  namespace: dr-prod
spec:
  protocol: C
  storageClassMappings:
    fast-ssd: fast-ssd-dr
  primaryEndpoints:
    - 192.168.10.11:7000
    - 192.168.10.12:7000
  drEndpoints:
    - 192.168.20.11:7000
    - 192.168.20.12:7000

Example 2 — ProtectionGroup with SLA

This groups two VMs into a single failover unit with a 30-second RPO target and 10-minute RTO target.

apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionGroup
metadata:
  name: pg-web-tier
  namespace: dr-prod
spec:
  virtualMachines:
    - name: web-vm-1
      namespace: production
    - name: web-vm-2
      namespace: production
  sla:
    rpoTargetSeconds: 30
    rtoTargetSeconds: 600

Example 3 — ProtectionRequest for a single VM

apiVersion: siterecovery.trilio.io/v1alpha1
kind: ProtectionRequest
metadata:
  name: web-vm-1-protect
  namespace: dr-prod
spec:
  vmName: web-vm-1
  vmNamespace: production
  replicationPolicyRef: policy-sync

After applying, watch the phase progress:

kubectl get protectionrequest web-vm-1-protect --namespace dr-prod -w

Expected progression:

NAME                  PHASE
web-vm-1-protect      Pending
web-vm-1-protect      Validating
web-vm-1-protect      Syncing
web-vm-1-protect      Protected

Example 4 — Inspecting ReplicationGroupStatus

kubectl get replicationgroupstatus pg-web-tier-replication \
  --namespace dr-prod -o yaml

Expected status when all volumes are synchronized:

status:
  rpoStatus: Met
  aggregated:
    connectionState: Connected
    totalVolumeCount: 2
    syncedVolumeCount: 2
  volumeReplicationStatuses:
    - drbdResourceName: drbd-production-web-vm-1
      vmName: web-vm-1
      pvcName: web-vm-1-boot-disk
      outOfSyncBytes: 0
      connectionState: Connected
      replicationState: Established
      estimatedRPOSeconds: 0
      lastSyncedAt: "2026-06-10T12:00:00Z"
  lastFullySyncedAt: "2026-06-10T12:00:00Z"
  lastPolledAt: "2026-06-10T12:00:15Z"

Example 5 — Planned failover

apiVersion: siterecovery.trilio.io/v1alpha1
kind: FailoverRequest
metadata:
  name: pg-web-tier-failover
  namespace: dr-prod
spec:
  protectionGroupRef: pg-web-tier
  type: Planned
  targetCluster: cluster2

Example 6 — Non-disruptive TestFailover (DRBD Operator model only)

apiVersion: siterecovery.trilio.io/v1alpha1
kind: TestFailover
metadata:
  name: pg-web-tier-test
  namespace: dr-prod
spec:
  protectionGroupRef: pg-web-tier
  targetNamespace: dr-test-isolation

Example 7 — Querying RPOEvents for a protection group

kubectl get rpoevents --namespace dr-prod \
  -l siterecovery.trilio.io/protection-group=pg-web-tier \
  -o yaml

An RPOEvent created when replication fell out of sync looks like:

apiVersion: siterecovery.trilio.io/v1alpha1
kind: RPOEvent
metadata:
  name: pg-web-tier-1749556800
  namespace: dr-prod
  labels:
    siterecovery.trilio.io/protection-group: pg-web-tier
    siterecovery.trilio.io/rpo-event-type: RPOViolated
spec:
  protectionGroupRef:
    name: pg-web-tier
    namespace: dr-prod
  eventType: RPOViolated
  previousRPOStatus: Met
  newRPOStatus: Violated
  violationReason: ProtocolCOutOfSync
  outOfSyncBytesAtEvent: 4096
  message: >-
    RPO status changed from Met to Violated: Synchronous replication has
    pending data. Check for network or I/O issues.

Troubleshooting

Use the following patterns to diagnose common CRD-related failures. For deeper diagnostics, collect a support bundle with oc adm must-gather --image=<tsr-gather-image>, which collects logs, CRD manifests, and status from the quorum cluster and all connected workload clusters.


ProtectionRequest stuck in Pending

Symptom: kubectl get protectionrequest <name> --namespace dr-prod shows PHASE: Pending for more than a few minutes.

Likely cause: The referenced DRBDReplicationPolicy does not exist in the same namespace, or the drbd-node-agent DaemonSet pods on the workload clusters are not running.

Fix:

  1. Confirm the policy exists: kubectl get drbdreplicationpolicy --namespace dr-prod
  2. Check agent pods on both workload clusters:
    kubectl get pods -n trilio-system \
      -l app.kubernetes.io/component=drbd-node-agent
    
  3. Inspect events on the ProtectionRequest:
    kubectl describe protectionrequest <name> --namespace dr-prod
    

ReplicationGroupStatus shows rpoStatus: Unknown

Symptom: status.rpoStatus is Unknown and status.aggregated.connectionState is Degraded.

Likely cause: The replication monitor cannot observe DRBD statistics for any volume in the group. In DRBD Operator mode this typically means no DRBDResource CRs exist yet on the workload clusters (protection has not completed), or the quorum control plane cannot reach the workload cluster API servers.

Fix:

  1. Confirm that ProtectionRequest resources for all VMs in the group have reached the Protected phase.
  2. Verify that DRBDResource CRs exist on both workload clusters in the VM's own namespace:
    kubectl get drbdresource --namespace <vm-namespace>
    
  3. Check that TCP port 6443 is open from the quorum cluster to both workload cluster API servers.
  4. Inspect the replication monitor logs:
    kubectl logs -n dr-prod \
      -l app.kubernetes.io/name=site-recovery-quorum-control-plane \
      --container replication-monitor
    

ReplicationGroupStatus shows rpoStatus: Violated with violationReason: ProtocolCOutOfSync

Symptom: The protection group is using Protocol C (synchronous) but outOfSyncBytes is non-zero.

Likely cause: Network latency between primary and DR clusters has exceeded ~50ms round-trip, causing DRBD to fall behind. A transient I/O spike on the primary can also cause this.

Fix:

  1. Measure current round-trip latency between primary and DR worker nodes.
  2. Check for packet loss or network congestion on the replication path (TCP ports 7000–7999).
  3. If latency is consistently above 50ms, consider switching to Protocol A by updating the DRBDReplicationPolicy.
  4. Monitor RPOEvent resources to understand the history and frequency of violations:
    kubectl get rpoevents --namespace dr-prod \
      -l siterecovery.trilio.io/protection-group=<pg-name>
    

FailoverRequest does not progress

Symptom: A FailoverRequest is created but VMs do not start on the target cluster.

Likely cause: The failover controller (part of site-recovery-quorum-control-plane) is not running, or the ProtectionGroup referenced by the request has rpoStatus: Violated and a planned failover was attempted (which requires zero data loss).

Fix:

  1. Confirm the quorum control plane pods are running:
    kubectl get pods --namespace dr-prod \
      -l app.kubernetes.io/name=site-recovery-quorum-control-plane
    
  2. Check events on the FailoverRequest:
    kubectl describe failoverrequest <name> --namespace dr-prod
    
  3. For unplanned failover of an out-of-sync group, ensure spec.type is Unplanned.

TestFailover not supported error

Symptom: Creating a TestFailover resource produces a validation error or the controller ignores it.

Likely cause: Your deployment uses the LINSTOR model, not the DRBD Operator model. TestFailover is only supported in DRBD Operator deployments.

Fix: Confirm your deployment model. Test failover requires the DRBD Operator to be installed on both workload clusters.


RPOEvents accumulating rapidly

Symptom: Many RPOEvent resources are created in a short time for the same protection group.

Likely cause: The replication state is oscillating between Met and Violated rapidly, typically due to intermittent network connectivity between clusters.

Fix:

  1. Inspect the violationReason field across recent events to identify a pattern.
  2. Check network stability between the primary and DR worker nodes on ports 7000–7999.
  3. Review DRBDResource status on both clusters for the affected VMs:
    kubectl get drbdresource --namespace <vm-namespace> -o yaml
    
  4. If the issue persists, collect a support bundle:
    oc adm must-gather --image=<tsr-gather-image> \
      --dest-dir=./tsr-bundle