Custom Resource Reference
Complete API reference for all site recovery custom resources
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.
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.
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.
Each CRD has its own spec fields. The table below summarizes the key configurable fields per resource and their effects.
ProtectionGroup
| Field | Type | Default | Description |
|---|---|---|---|
spec.virtualMachines | []VMRef | required | List of {name, namespace} pairs identifying the VMs to protect as a group. |
spec.sourceCluster | ClusterID | "" | 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.rtoTargetSeconds | int32 | required if sla is set | Target recovery time in seconds. Minimum value: 1. |
spec.sla.rpoTargetSeconds | int32 | 0 | Target recovery point in seconds. 0 means RPO=0 (synchronous). The replication monitor propagates this value to the associated ReplicationGroupStatus. |
ProtectionRequest (DRBD Operator model)
| Field | Type | Default | Description |
|---|---|---|---|
spec.vmName | string | required | Name of the VirtualMachine resource to protect. |
spec.vmNamespace | string | required | Namespace containing the VM. |
spec.replicationPolicyRef | string | required | Name of the DRBDReplicationPolicy to use for this VM. The policy must already exist. |
FailoverRequest
| Field | Type | Default | Description |
|---|---|---|---|
spec.protectionGroupRef | string | required | Name of the ProtectionGroup to fail over. |
spec.type | string | required | Planned (graceful, zero data loss) or Unplanned (disaster, immediate promotion). |
spec.targetCluster | ClusterID | required | The cluster (cluster1 or cluster2) to which VMs will be started after failover. |
TestFailover (DRBD Operator model only)
| Field | Type | Default | Description |
|---|---|---|---|
spec.protectionGroupRef | string | required | Name of the ProtectionGroup to test. |
spec.targetNamespace | string | required | Isolated namespace on the DR cluster where snapshot-based VM copies are created. |
DRBDReplicationPolicy (DRBD Operator model)
| Field | Type | Default | Description |
|---|---|---|---|
spec.protocol | string | required | Replication protocol: A (asynchronous, RPO=seconds) or C (synchronous, RPO=0). Protocol C requires network round-trip latency below ~50ms between clusters. |
spec.storageClassMappings | map[string]string | required | Maps primary cluster storage class names to DR cluster storage class names. |
spec.primaryEndpoints | []string | required | DRBD endpoint addresses (IP:port) on the primary cluster worker nodes. |
spec.drEndpoints | []string | required | DRBD endpoint addresses (IP:port) on the DR cluster worker nodes. |
ReplicationGroupStatus
| Field | Type | Default | Description |
|---|---|---|---|
spec.protectionGroupRef | PGRef | required | {name, namespace} of the owning ProtectionGroup. |
spec.replicationProtocol | string | C | Replication protocol (A or C). Propagated from the DRBDReplicationPolicy. |
spec.rpoObjectiveSeconds | int32 | 0 | RPO 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.pollingIntervalSeconds | int32 | 15 | How often (in seconds) the replication monitor scrapes DRBD metrics and patches this resource's status. Overrides the deployment-wide DEFAULT_SCRAPE_INTERVAL. |
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.rpoStatus—Met,Degraded,Violated, orUnknownstatus.aggregated.connectionState—ConnectedorDegradedstatus.aggregated.totalVolumeCount/syncedVolumeCountstatus.volumeReplicationStatuses[]— per-VM breakdown withoutOfSyncBytes,connectionState,replicationState, andestimatedRPOSecondsstatus.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.
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.
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:
- Confirm the policy exists:
kubectl get drbdreplicationpolicy --namespace dr-prod - Check agent pods on both workload clusters:
kubectl get pods -n trilio-system \ -l app.kubernetes.io/component=drbd-node-agent - 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:
- Confirm that
ProtectionRequestresources for all VMs in the group have reached theProtectedphase. - Verify that
DRBDResourceCRs exist on both workload clusters in the VM's own namespace:kubectl get drbdresource --namespace <vm-namespace> - Check that TCP port 6443 is open from the quorum cluster to both workload cluster API servers.
- 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:
- Measure current round-trip latency between primary and DR worker nodes.
- Check for packet loss or network congestion on the replication path (TCP ports 7000–7999).
- If latency is consistently above 50ms, consider switching to Protocol A by updating the
DRBDReplicationPolicy. - Monitor
RPOEventresources 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:
- Confirm the quorum control plane pods are running:
kubectl get pods --namespace dr-prod \ -l app.kubernetes.io/name=site-recovery-quorum-control-plane - Check events on the
FailoverRequest:kubectl describe failoverrequest <name> --namespace dr-prod - For unplanned failover of an out-of-sync group, ensure
spec.typeisUnplanned.
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:
- Inspect the
violationReasonfield across recent events to identify a pattern. - Check network stability between the primary and DR worker nodes on ports 7000–7999.
- Review
DRBDResourcestatus on both clusters for the affected VMs:kubectl get drbdresource --namespace <vm-namespace> -o yaml - If the issue persists, collect a support bundle:
oc adm must-gather --image=<tsr-gather-image> \ --dest-dir=./tsr-bundle